--- /dev/null
+/* Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include "gdkpixbufutilsprivate.h"
+
+static GdkPixbuf *
+load_from_stream (GdkPixbufLoader *loader,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ gssize n_read;
+ guchar buffer[65536];
+ gboolean res;
+
+ res = TRUE;
+ while (1)
+ {
+ n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
+ if (n_read < 0)
+ {
+ res = FALSE;
+ error = NULL; /* Ignore further errors */
+ break;
+ }
+
+ if (n_read == 0)
+ break;
+
+ if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
+ {
+ res = FALSE;
+ error = NULL;
+ break;
+ }
+ }
+
+ if (!gdk_pixbuf_loader_close (loader, error))
+ {
+ res = FALSE;
+ error = NULL;
+ }
+
+ pixbuf = NULL;
+
+ if (res)
+ {
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (pixbuf)
+ g_object_ref (pixbuf);
+ }
+
+ return pixbuf;
+}
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ gpointer data)
+{
+ double *scale = data;
+
+ width = MAX (*scale * width, 1);
+ height = MAX (*scale * height, 1);
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+/* Like gdk_pixbuf_new_from_stream_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
+ const char *format,
+ double scale,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf;
+
+ if (format)
+ {
+ loader = gdk_pixbuf_loader_new_with_type (format, error);
+ if (!loader)
+ return NULL;
+ }
+ else
+ loader = gdk_pixbuf_loader_new ();
+
+ if (scale != 0)
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (size_prepared_cb), &scale);
+
+ pixbuf = load_from_stream (loader, stream, cancellable, error);
+
+ g_object_unref (loader);
+
+ return pixbuf;
+}
+
+static void
+size_prepared_cb2 (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ gpointer data)
+{
+ int *scales = data;
+
+ if (scales[2]) /* keep same aspect ratio as original, while fitting in given size */
+ {
+ double aspect = (double) height / width;
+
+ /* First use given width and calculate size */
+ width = scales[0];
+ height = scales[0] * aspect;
+
+ /* Check if it fits given height, otherwise scale down */
+ if (height > scales[1])
+ {
+ width *= (double) scales[1] / height;
+ height = scales[1];
+ }
+ }
+ else
+ {
+ width = scales[0];
+ height = scales[1];
+ }
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream,
+ const char *format,
+ int width,
+ int height,
+ gboolean aspect,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf;
+ int scales[3];
+
+ if (format)
+ {
+ loader = gdk_pixbuf_loader_new_with_type (format, error);
+ if (!loader)
+ return NULL;
+ }
+ else
+ loader = gdk_pixbuf_loader_new ();
+
+ scales[0] = width;
+ scales[1] = height;
+ scales[2] = aspect;
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (size_prepared_cb2), scales);
+
+ pixbuf = load_from_stream (loader, stream, cancellable, error);
+
+ g_object_unref (loader);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream (GInputStream *stream,
+ const char *format,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error);
+}
+
+/* Like gdk_pixbuf_new_from_resource_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource_scaled (const char *resource_path,
+ const char *format,
+ double scale,
+ GError **error)
+{
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+
+ stream = g_resources_open_stream (resource_path, 0, error);
+ if (stream == NULL)
+ return NULL;
+
+ pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error);
+ g_object_unref (stream);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource (const char *resource_path,
+ const char *format,
+ GError **error)
+{
+ return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error);
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource_at_scale (const char *resource_path,
+ const char *format,
+ int width,
+ int height,
+ gboolean preserve_aspect,
+ GError **error)
+{
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+
+ stream = g_resources_open_stream (resource_path, 0, error);
+ if (stream == NULL)
+ return NULL;
+
+ pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error);
+ g_object_unref (stream);
+
+ return pixbuf;
+
+}
+
+static GdkPixbuf *
+load_symbolic_svg (const char *escaped_file_data,
+ int width,
+ int height,
+ const char *icon_width_str,
+ const char *icon_height_str,
+ const char *fg_string,
+ const char *success_color_string,
+ const char *warning_color_string,
+ const char *error_color_string,
+ GError **error)
+{
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+ char *data;
+
+ data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ "<svg version=\"1.1\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
+ " width=\"", icon_width_str, "\"\n"
+ " height=\"", icon_height_str, "\">\n"
+ " <style type=\"text/css\">\n"
+ " rect,circle,path {\n"
+ " fill: ", fg_string," !important;\n"
+ " }\n"
+ " .warning {\n"
+ " fill: ", warning_color_string, " !important;\n"
+ " }\n"
+ " .error {\n"
+ " fill: ", error_color_string ," !important;\n"
+ " }\n"
+ " .success {\n"
+ " fill: ", success_color_string, " !important;\n"
+ " }\n"
+ " </style>\n"
+ " <xi:include href=\"data:text/xml;base64,", escaped_file_data, "\"/>\n"
+ "</svg>",
+ NULL);
+
+ stream = g_memory_input_stream_new_from_data (data, -1, g_free);
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
+ g_object_unref (stream);
+
+ return pixbuf;
+}
+
+static void
+rgba_to_pixel (const GdkRGBA *rgba,
+ guint8 pixel[4])
+{
+ pixel[0] = rgba->red * 255;
+ pixel[1] = rgba->green * 255;
+ pixel[2] = rgba->blue * 255;
+ pixel[3] = 255;
+}
+
+GdkPixbuf *
+gtk_color_symbolic_pixbuf (GdkPixbuf *symbolic,
+ const GdkRGBA *fg_color,
+ const GdkRGBA *success_color,
+ const GdkRGBA *warning_color,
+ const GdkRGBA *error_color)
+{
+ int width, height, x, y, src_stride, dst_stride;
+ guchar *src_data, *dst_data;
+ guchar *src_row, *dst_row;
+ int alpha;
+ GdkPixbuf *colored;
+ guint8 fg_pixel[4], success_pixel[4], warning_pixel[4], error_pixel[4];
+
+ alpha = fg_color->alpha * 255;
+
+ rgba_to_pixel (fg_color, fg_pixel);
+ rgba_to_pixel (success_color, success_pixel);
+ rgba_to_pixel (warning_color, warning_pixel);
+ rgba_to_pixel (error_color, error_pixel);
+
+ width = gdk_pixbuf_get_width (symbolic);
+ height = gdk_pixbuf_get_height (symbolic);
+
+ colored = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+
+ src_stride = gdk_pixbuf_get_rowstride (symbolic);
+ src_data = gdk_pixbuf_get_pixels (symbolic);
+
+ dst_data = gdk_pixbuf_get_pixels (colored);
+ dst_stride = gdk_pixbuf_get_rowstride (colored);
+ for (y = 0; y < height; y++)
+ {
+ src_row = src_data + src_stride * y;
+ dst_row = dst_data + dst_stride * y;
+ for (x = 0; x < width; x++)
+ {
+ guint r, g, b, a;
+ int c1, c2, c3, c4;
+
+ a = src_row[3];
+ dst_row[3] = a * alpha / 255;
+
+ if (a == 0)
+ {
+ dst_row[0] = 0;
+ dst_row[1] = 0;
+ dst_row[2] = 0;
+ }
+ else
+ {
+ c2 = src_row[0];
+ c3 = src_row[1];
+ c4 = src_row[2];
+
+ if (c2 == 0 && c3 == 0 && c4 == 0)
+ {
+ dst_row[0] = fg_pixel[0];
+ dst_row[1] = fg_pixel[1];
+ dst_row[2] = fg_pixel[2];
+ }
+ else
+ {
+ c1 = 255 - c2 - c3 - c4;
+
+ r = fg_pixel[0] * c1 + success_pixel[0] * c2 + warning_pixel[0] * c3 + error_pixel[0] * c4;
+ g = fg_pixel[1] * c1 + success_pixel[1] * c2 + warning_pixel[1] * c3 + error_pixel[1] * c4;
+ b = fg_pixel[2] * c1 + success_pixel[2] * c2 + warning_pixel[2] * c3 + error_pixel[2] * c4;
+
+ dst_row[0] = r / 255;
+ dst_row[1] = g / 255;
+ dst_row[2] = b / 255;
+ }
+ }
+
+ src_row += 4;
+ dst_row += 4;
+ }
+ }
+
+ return colored;
+}
+
+static void
+extract_plane (GdkPixbuf *src,
+ GdkPixbuf *dst,
+ int from_plane,
+ int to_plane)
+{
+ guchar *src_data, *dst_data;
+ int width, height, src_stride, dst_stride;
+ guchar *src_row, *dst_row;
+ int x, y;
+
+ width = gdk_pixbuf_get_width (src);
+ height = gdk_pixbuf_get_height (src);
+
+ g_assert (width <= gdk_pixbuf_get_width (dst));
+ g_assert (height <= gdk_pixbuf_get_height (dst));
+
+ src_stride = gdk_pixbuf_get_rowstride (src);
+ src_data = gdk_pixbuf_get_pixels (src);
+
+ dst_data = gdk_pixbuf_get_pixels (dst);
+ dst_stride = gdk_pixbuf_get_rowstride (dst);
+
+ for (y = 0; y < height; y++)
+ {
+ src_row = src_data + src_stride * y;
+ dst_row = dst_data + dst_stride * y;
+ for (x = 0; x < width; x++)
+ {
+ dst_row[to_plane] = src_row[from_plane];
+ src_row += 4;
+ dst_row += 4;
+ }
+ }
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_data (const char *file_data,
+ gsize file_len,
+ int width,
+ int height,
+ double scale,
+ const char *debug_output_basename,
+ GError **error)
+
+{
+ const char *r_string = "rgb(255,0,0)";
+ const char *g_string = "rgb(0,255,0)";
+ char *icon_width_str;
+ char *icon_height_str;
+ GdkPixbuf *loaded;
+ GdkPixbuf *pixbuf = NULL;
+ int plane;
+ int icon_width, icon_height;
+ char *escaped_file_data;
+
+ /* Fetch size from the original icon */
+ {
+ GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
+ GdkPixbuf *reference = gdk_pixbuf_new_from_stream (stream, NULL, error);
+
+ g_object_unref (stream);
+
+ if (!reference)
+ return NULL;
+
+ icon_width = gdk_pixbuf_get_width (reference);
+ icon_height = gdk_pixbuf_get_height (reference);
+ g_object_unref (reference);
+ }
+
+ escaped_file_data = g_base64_encode ((guchar *) file_data, file_len);
+ icon_width_str = g_strdup_printf ("%d", icon_width);
+ icon_height_str = g_strdup_printf ("%d", icon_height);
+
+ if (width == 0)
+ width = icon_width * scale;
+ if (height == 0)
+ height = icon_height * scale;
+
+ for (plane = 0; plane < 3; plane++)
+ {
+ /* Here we render the svg with all colors solid, this should
+ * always make the alpha channel the same and it should match
+ * the final alpha channel for all possible renderings. We
+ * Just use it as-is for final alpha.
+ *
+ * For the 3 non-fg colors, we render once each with that
+ * color as red, and every other color as green. The resulting
+ * red will describe the amount of that color is in the
+ * opaque part of the color. We store these as the rgb
+ * channels, with the color of the fg being implicitly
+ * the "rest", as all color fractions should add up to 1.
+ */
+ loaded = load_symbolic_svg (escaped_file_data, width, height,
+ icon_width_str,
+ icon_height_str,
+ g_string,
+ plane == 0 ? r_string : g_string,
+ plane == 1 ? r_string : g_string,
+ plane == 2 ? r_string : g_string,
+ error);
+ if (loaded == NULL)
+ goto out;
+
+ if (debug_output_basename)
+ {
+ char *filename;
+
+ filename = g_strdup_printf ("%s.debug%d.png", debug_output_basename, plane);
+ g_print ("Writing %s\n", filename);
+ gdk_pixbuf_save (loaded, filename, "png", NULL, NULL);
+ g_free (filename);
+ }
+
+ if (pixbuf == NULL)
+ {
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ gdk_pixbuf_get_width (loaded),
+ gdk_pixbuf_get_height (loaded));
+ gdk_pixbuf_fill (pixbuf, 0);
+ }
+
+ if (plane == 0)
+ extract_plane (loaded, pixbuf, 3, 3);
+
+ extract_plane (loaded, pixbuf, 0, plane);
+
+ g_object_unref (loaded);
+ }
+
+ g_free (escaped_file_data);
+
+out:
+ g_free (icon_width_str);
+ g_free (icon_height_str);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_resource (const char *path,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GBytes *bytes;
+ const char *data;
+ gsize size;
+ GdkPixbuf *pixbuf;
+
+ bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
+ if (bytes == NULL)
+ return NULL;
+
+ data = g_bytes_get_data (bytes, &size);
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+ g_bytes_unref (bytes);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_path (const char *path,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ char *data;
+ gsize size;
+ GdkPixbuf *pixbuf;
+
+ if (!g_file_get_contents (path, &data, &size, error))
+ return NULL;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+ g_free (data);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_file (GFile *file,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ char *data;
+ gsize size;
+ GdkPixbuf *pixbuf;
+
+ if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
+ return NULL;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+ g_free (data);
+
+ return pixbuf;
+}
+
+GdkTexture *
+gtk_load_symbolic_texture_from_resource (const char *path)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture;
+
+ pixbuf = _gdk_pixbuf_new_from_resource (path, "png", NULL);
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return texture;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_resource (const char *path,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture = NULL;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
+ if (pixbuf)
+ {
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ }
+
+ return texture;
+}
+
+GdkTexture *
+gtk_load_symbolic_texture_from_file (GFile *file)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture;
+ GInputStream *stream;
+
+ stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ if (stream == NULL)
+ return NULL;
+
+ pixbuf = _gdk_pixbuf_new_from_stream (stream, "png", NULL, NULL);
+ g_object_unref (stream);
+ if (pixbuf == NULL)
+ return NULL;
+
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return texture;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_file (GFile *file,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return texture;
+}
--- /dev/null
+/* gtkiconcachevalidator.c
+ * Copyright (C) 2007 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+#include "gtkiconcachevalidatorprivate.h"
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+
+#define VERBOSE(x)
+
+#define check(name,condition) \
+ if (!(condition)) \
+ { \
+ VERBOSE(g_message ("bad %s", (name))); \
+ return FALSE; \
+ }
+
+static inline gboolean
+get_uint16 (CacheInfo *info,
+ guint32 offset,
+ guint16 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static inline gboolean
+get_uint32 (CacheInfo *info,
+ guint32 offset,
+ guint32 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+check_version (CacheInfo *info)
+{
+ guint16 major, minor;
+
+ check ("major version", get_uint16 (info, 0, &major) && major == 1);
+ check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
+
+ return TRUE;
+}
+
+static gboolean
+check_string (CacheInfo *info,
+ guint32 offset)
+{
+ check ("string offset", offset < info->cache_size);
+
+ if (info->flags & CHECK_STRINGS)
+ {
+ int i;
+ char c;
+
+ /* assume no string is longer than 1k */
+ for (i = 0; i < 1024; i++)
+ {
+ check ("string offset", offset + i < info->cache_size)
+ c = *(info->cache + offset + i);
+ if (c == '\0')
+ break;
+ check ("string content", g_ascii_isgraph (c));
+ }
+ check ("string length", i < 1024);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_string_utf8 (CacheInfo *info,
+ guint32 offset)
+{
+ check ("string offset", offset < info->cache_size);
+
+ if (info->flags & CHECK_STRINGS)
+ {
+ int i;
+ char c;
+
+ /* assume no string is longer than 1k */
+ for (i = 0; i < 1024; i++)
+ {
+ check ("string offset", offset + i < info->cache_size)
+ c = *(info->cache + offset + i);
+ if (c == '\0')
+ break;
+ }
+ check ("string length", i < 1024);
+ check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_directory_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 directory_offset;
+ int i;
+
+ check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
+
+ for (i = 0; i < info->n_directories; i++)
+ {
+ check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
+ if (!check_string (info, directory_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_pixel_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 type;
+ guint32 length;
+
+ check ("offset, pixel data type", get_uint32 (info, offset, &type));
+ check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
+
+ check ("pixel data type", type == 0);
+ check ("pixel data length", offset + 8 + length < info->cache_size);
+
+ if (info->flags & CHECK_PIXBUFS)
+ {
+ GdkPixdata data;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ check ("pixel data", gdk_pixdata_deserialize (&data, length,
+ (const guint8*)info->cache + offset + 8,
+ NULL));
+G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_embedded_rect (CacheInfo *info,
+ guint32 offset)
+{
+ check ("embedded rect", offset + 4 < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_attach_point_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_attach_points;
+
+ check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
+ check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_display_name_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_display_names, ofs;
+ int i;
+
+ check ("offset, display name list",
+ get_uint32 (info, offset, &n_display_names));
+ for (i = 0; i < n_display_names; i++)
+ {
+ get_uint32(info, offset + 4 + 8 * i, &ofs);
+ if (!check_string (info, ofs))
+ return FALSE;
+ get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
+ if (!check_string_utf8 (info, ofs))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_meta_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 embedded_rect_offset;
+ guint32 attach_point_list_offset;
+ guint32 display_name_list_offset;
+
+ check ("offset, embedded rect",
+ get_uint32 (info, offset, &embedded_rect_offset));
+ check ("offset, attach point list",
+ get_uint32 (info, offset + 4, &attach_point_list_offset));
+ check ("offset, display name list",
+ get_uint32 (info, offset + 8, &display_name_list_offset));
+
+ if (embedded_rect_offset != 0)
+ {
+ if (!check_embedded_rect (info, embedded_rect_offset))
+ return FALSE;
+ }
+
+ if (attach_point_list_offset != 0)
+ {
+ if (!check_attach_point_list (info, attach_point_list_offset))
+ return FALSE;
+ }
+
+ if (display_name_list_offset != 0)
+ {
+ if (!check_display_name_list (info, display_name_list_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 pixel_data_offset;
+ guint32 meta_data_offset;
+
+ check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
+ check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
+
+ if (pixel_data_offset != 0)
+ {
+ if (!check_pixel_data (info, pixel_data_offset))
+ return FALSE;
+ }
+ if (meta_data_offset != 0)
+ {
+ if (!check_meta_data (info, meta_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image (CacheInfo *info,
+ guint32 offset)
+{
+ guint16 index;
+ guint16 flags;
+ guint32 image_data_offset;
+
+ check ("offset, image index", get_uint16 (info, offset, &index));
+ check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
+ check ("offset, image data offset",
+ get_uint32 (info, offset + 4, &image_data_offset));
+
+ check ("image index", index < info->n_directories);
+ check ("image flags", flags < 16);
+
+ if (image_data_offset != 0)
+ {
+ if (!check_image_data (info, image_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_images;
+ int i;
+
+ check ("offset, image list", get_uint32 (info, offset, &n_images));
+
+ for (i = 0; i < n_images; i++)
+ {
+ if (!check_image (info, offset + 4 + 8 * i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_icon (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 chain_offset;
+ guint32 name_offset;
+ guint32 image_list_offset;
+
+ check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
+ check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
+ check ("offset, icon image list", get_uint32 (info, offset + 8,
+ &image_list_offset));
+
+ if (!check_string (info, name_offset))
+ return FALSE;
+ if (!check_image_list (info, image_list_offset))
+ return FALSE;
+ if (chain_offset != 0xffffffff)
+ {
+ if (!check_icon (info, chain_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_hash (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_buckets, icon_offset;
+ int i;
+
+ check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
+
+ for (i = 0; i < n_buckets; i++)
+ {
+ check ("offset, hash chain",
+ get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
+ if (icon_offset != 0xffffffff)
+ {
+ if (!check_icon (info, icon_offset))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gtk_icon_cache_validate:
+ * @info: a CacheInfo structure
+ *
+ * Validates the icon cache passed in the @cache and
+ * @cache_size fields of the @info structure. The
+ * validator checks that offsets specified in the
+ * cache do not point outside the mapped area, that
+ * strings look reasonable, and that pixbufs can
+ * be deserialized. The amount of validation can
+ * be controlled with the @flags field.
+ *
+ * Returns: %TRUE if the cache is valid
+ */
+gboolean
+gtk_icon_cache_validate (CacheInfo *info)
+{
+ guint32 hash_offset;
+ guint32 directory_list_offset;
+
+ if (!check_version (info))
+ return FALSE;
+ check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
+ check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
+ if (!check_directory_list (info, directory_list_offset))
+ return FALSE;
+
+ if (!check_hash (info, hash_offset))
+ return FALSE;
+
+ return TRUE;
+}
+
# introspected
gtk_private_sources = files([
'fnmatch.c',
- 'tools/gdkpixbufutils.c',
+ 'gdkpixbufutils.c',
'gsettings-mapping.c',
'gtkaccessibleattributeset.c',
'gtkaccessiblevalue.c',
'gtkgladecatalog.c',
'gtkhsla.c',
'gtkiconcache.c',
- 'tools/gtkiconcachevalidator.c',
+ 'gtkiconcachevalidator.c',
'gtkiconhelper.c',
'gtkkineticscrolling.c',
'gtkmagnifier.c',
link_with: [libgtk_static, libgtk_css, libgdk, libgsk ],
link_args: common_ldflags,
)
-
-subdir('tools')
+++ /dev/null
-/* encodesymbolic.c
- * Copyright (C) 2014 Alexander Larsson <alexl@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <glib.h>
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-#endif
-#include <errno.h>
-#include <stdlib.h>
-#include <locale.h>
-
-#include "gdkpixbufutilsprivate.h"
-
-static char *output_dir = NULL;
-
-static gboolean debug;
-
-static GOptionEntry args[] = {
- { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
- { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Generate debug output") },
- { NULL }
-};
-
-int
-main (int argc, char **argv)
-{
- char *path, *basename, *pngpath, *pngfile, *dot;
- GOptionContext *context;
- GdkPixbuf *symbolic;
- GError *error;
- int width, height;
- char **sizev;
- GFileOutputStream *out;
- GFile *dest;
- char *data;
- gsize len;
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- g_set_prgname ("gtk-encode-symbolic-svg");
-
- context = g_option_context_new ("[OPTION…] PATH WIDTHxHEIGHT");
- g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
- g_option_context_parse (context, &argc, &argv, NULL);
-
- if (argc < 3)
- {
- g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
- return 1;
- }
-
- width = 0;
- height = 0;
- sizev = g_strsplit (argv[2], "x", 0);
- if (g_strv_length (sizev) == 2)
- {
- width = atoi(sizev[0]);
- height = atoi(sizev[1]);
- }
- g_strfreev (sizev);
-
- if (width == 0 || height == 0)
- {
- g_printerr (_("Invalid size %s\n"), argv[2]);
- return 1;
- }
-
- path = argv[1];
-#ifdef G_OS_WIN32
- path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
- error = NULL;
- if (!g_file_get_contents (path, &data, &len, &error))
- {
- g_printerr (_("Can’t load file: %s\n"), error->message);
- return 1;
- }
-
- basename = g_path_get_basename (path);
-
- symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, debug ? basename : NULL, &error);
- if (symbolic == NULL)
- {
- g_printerr (_("Can’t load file: %s\n"), error->message);
- return 1;
- }
-
- g_free (data);
-
- dot = strrchr (basename, '.');
- if (dot != NULL)
- *dot = 0;
- pngfile = g_strconcat (basename, ".symbolic.png", NULL);
- g_free (basename);
-
- if (output_dir != NULL)
- pngpath = g_build_filename (output_dir, pngfile, NULL);
- else
- pngpath = g_strdup (pngfile);
-
- g_free (pngfile);
-
- dest = g_file_new_for_path (pngpath);
-
-
- out = g_file_replace (dest,
- NULL, FALSE,
- G_FILE_CREATE_REPLACE_DESTINATION,
- NULL, &error);
- if (out == NULL)
- {
- g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
- return 1;
- }
-
- if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
- {
- g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
- return 1;
- }
-
- if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
- {
- g_printerr (_("Can’t close stream"));
- return 1;
- }
-
- g_object_unref (out);
- g_free (pngpath);
-
- return 0;
-}
+++ /dev/null
-/* Copyright (C) 2016 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <gdk/gdk.h>
-#include "gdkpixbufutilsprivate.h"
-
-static GdkPixbuf *
-load_from_stream (GdkPixbufLoader *loader,
- GInputStream *stream,
- GCancellable *cancellable,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- gssize n_read;
- guchar buffer[65536];
- gboolean res;
-
- res = TRUE;
- while (1)
- {
- n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
- if (n_read < 0)
- {
- res = FALSE;
- error = NULL; /* Ignore further errors */
- break;
- }
-
- if (n_read == 0)
- break;
-
- if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
- {
- res = FALSE;
- error = NULL;
- break;
- }
- }
-
- if (!gdk_pixbuf_loader_close (loader, error))
- {
- res = FALSE;
- error = NULL;
- }
-
- pixbuf = NULL;
-
- if (res)
- {
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- if (pixbuf)
- g_object_ref (pixbuf);
- }
-
- return pixbuf;
-}
-
-static void
-size_prepared_cb (GdkPixbufLoader *loader,
- int width,
- int height,
- gpointer data)
-{
- double *scale = data;
-
- width = MAX (*scale * width, 1);
- height = MAX (*scale * height, 1);
-
- gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
-/* Like gdk_pixbuf_new_from_stream_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
- const char *format,
- double scale,
- GCancellable *cancellable,
- GError **error)
-{
- GdkPixbufLoader *loader;
- GdkPixbuf *pixbuf;
-
- if (format)
- {
- loader = gdk_pixbuf_loader_new_with_type (format, error);
- if (!loader)
- return NULL;
- }
- else
- loader = gdk_pixbuf_loader_new ();
-
- if (scale != 0)
- g_signal_connect (loader, "size-prepared",
- G_CALLBACK (size_prepared_cb), &scale);
-
- pixbuf = load_from_stream (loader, stream, cancellable, error);
-
- g_object_unref (loader);
-
- return pixbuf;
-}
-
-static void
-size_prepared_cb2 (GdkPixbufLoader *loader,
- int width,
- int height,
- gpointer data)
-{
- int *scales = data;
-
- if (scales[2]) /* keep same aspect ratio as original, while fitting in given size */
- {
- double aspect = (double) height / width;
-
- /* First use given width and calculate size */
- width = scales[0];
- height = scales[0] * aspect;
-
- /* Check if it fits given height, otherwise scale down */
- if (height > scales[1])
- {
- width *= (double) scales[1] / height;
- height = scales[1];
- }
- }
- else
- {
- width = scales[0];
- height = scales[1];
- }
-
- gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream,
- const char *format,
- int width,
- int height,
- gboolean aspect,
- GCancellable *cancellable,
- GError **error)
-{
- GdkPixbufLoader *loader;
- GdkPixbuf *pixbuf;
- int scales[3];
-
- if (format)
- {
- loader = gdk_pixbuf_loader_new_with_type (format, error);
- if (!loader)
- return NULL;
- }
- else
- loader = gdk_pixbuf_loader_new ();
-
- scales[0] = width;
- scales[1] = height;
- scales[2] = aspect;
- g_signal_connect (loader, "size-prepared",
- G_CALLBACK (size_prepared_cb2), scales);
-
- pixbuf = load_from_stream (loader, stream, cancellable, error);
-
- g_object_unref (loader);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream (GInputStream *stream,
- const char *format,
- GCancellable *cancellable,
- GError **error)
-{
- return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error);
-}
-
-/* Like gdk_pixbuf_new_from_resource_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource_scaled (const char *resource_path,
- const char *format,
- double scale,
- GError **error)
-{
- GInputStream *stream;
- GdkPixbuf *pixbuf;
-
- stream = g_resources_open_stream (resource_path, 0, error);
- if (stream == NULL)
- return NULL;
-
- pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error);
- g_object_unref (stream);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource (const char *resource_path,
- const char *format,
- GError **error)
-{
- return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error);
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource_at_scale (const char *resource_path,
- const char *format,
- int width,
- int height,
- gboolean preserve_aspect,
- GError **error)
-{
- GInputStream *stream;
- GdkPixbuf *pixbuf;
-
- stream = g_resources_open_stream (resource_path, 0, error);
- if (stream == NULL)
- return NULL;
-
- pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error);
- g_object_unref (stream);
-
- return pixbuf;
-
-}
-
-static GdkPixbuf *
-load_symbolic_svg (const char *escaped_file_data,
- int width,
- int height,
- const char *icon_width_str,
- const char *icon_height_str,
- const char *fg_string,
- const char *success_color_string,
- const char *warning_color_string,
- const char *error_color_string,
- GError **error)
-{
- GInputStream *stream;
- GdkPixbuf *pixbuf;
- char *data;
-
- data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- "<svg version=\"1.1\"\n"
- " xmlns=\"http://www.w3.org/2000/svg\"\n"
- " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
- " width=\"", icon_width_str, "\"\n"
- " height=\"", icon_height_str, "\">\n"
- " <style type=\"text/css\">\n"
- " rect,circle,path {\n"
- " fill: ", fg_string," !important;\n"
- " }\n"
- " .warning {\n"
- " fill: ", warning_color_string, " !important;\n"
- " }\n"
- " .error {\n"
- " fill: ", error_color_string ," !important;\n"
- " }\n"
- " .success {\n"
- " fill: ", success_color_string, " !important;\n"
- " }\n"
- " </style>\n"
- " <xi:include href=\"data:text/xml;base64,", escaped_file_data, "\"/>\n"
- "</svg>",
- NULL);
-
- stream = g_memory_input_stream_new_from_data (data, -1, g_free);
- pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
- g_object_unref (stream);
-
- return pixbuf;
-}
-
-static void
-rgba_to_pixel (const GdkRGBA *rgba,
- guint8 pixel[4])
-{
- pixel[0] = rgba->red * 255;
- pixel[1] = rgba->green * 255;
- pixel[2] = rgba->blue * 255;
- pixel[3] = 255;
-}
-
-GdkPixbuf *
-gtk_color_symbolic_pixbuf (GdkPixbuf *symbolic,
- const GdkRGBA *fg_color,
- const GdkRGBA *success_color,
- const GdkRGBA *warning_color,
- const GdkRGBA *error_color)
-{
- int width, height, x, y, src_stride, dst_stride;
- guchar *src_data, *dst_data;
- guchar *src_row, *dst_row;
- int alpha;
- GdkPixbuf *colored;
- guint8 fg_pixel[4], success_pixel[4], warning_pixel[4], error_pixel[4];
-
- alpha = fg_color->alpha * 255;
-
- rgba_to_pixel (fg_color, fg_pixel);
- rgba_to_pixel (success_color, success_pixel);
- rgba_to_pixel (warning_color, warning_pixel);
- rgba_to_pixel (error_color, error_pixel);
-
- width = gdk_pixbuf_get_width (symbolic);
- height = gdk_pixbuf_get_height (symbolic);
-
- colored = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
-
- src_stride = gdk_pixbuf_get_rowstride (symbolic);
- src_data = gdk_pixbuf_get_pixels (symbolic);
-
- dst_data = gdk_pixbuf_get_pixels (colored);
- dst_stride = gdk_pixbuf_get_rowstride (colored);
- for (y = 0; y < height; y++)
- {
- src_row = src_data + src_stride * y;
- dst_row = dst_data + dst_stride * y;
- for (x = 0; x < width; x++)
- {
- guint r, g, b, a;
- int c1, c2, c3, c4;
-
- a = src_row[3];
- dst_row[3] = a * alpha / 255;
-
- if (a == 0)
- {
- dst_row[0] = 0;
- dst_row[1] = 0;
- dst_row[2] = 0;
- }
- else
- {
- c2 = src_row[0];
- c3 = src_row[1];
- c4 = src_row[2];
-
- if (c2 == 0 && c3 == 0 && c4 == 0)
- {
- dst_row[0] = fg_pixel[0];
- dst_row[1] = fg_pixel[1];
- dst_row[2] = fg_pixel[2];
- }
- else
- {
- c1 = 255 - c2 - c3 - c4;
-
- r = fg_pixel[0] * c1 + success_pixel[0] * c2 + warning_pixel[0] * c3 + error_pixel[0] * c4;
- g = fg_pixel[1] * c1 + success_pixel[1] * c2 + warning_pixel[1] * c3 + error_pixel[1] * c4;
- b = fg_pixel[2] * c1 + success_pixel[2] * c2 + warning_pixel[2] * c3 + error_pixel[2] * c4;
-
- dst_row[0] = r / 255;
- dst_row[1] = g / 255;
- dst_row[2] = b / 255;
- }
- }
-
- src_row += 4;
- dst_row += 4;
- }
- }
-
- return colored;
-}
-
-static void
-extract_plane (GdkPixbuf *src,
- GdkPixbuf *dst,
- int from_plane,
- int to_plane)
-{
- guchar *src_data, *dst_data;
- int width, height, src_stride, dst_stride;
- guchar *src_row, *dst_row;
- int x, y;
-
- width = gdk_pixbuf_get_width (src);
- height = gdk_pixbuf_get_height (src);
-
- g_assert (width <= gdk_pixbuf_get_width (dst));
- g_assert (height <= gdk_pixbuf_get_height (dst));
-
- src_stride = gdk_pixbuf_get_rowstride (src);
- src_data = gdk_pixbuf_get_pixels (src);
-
- dst_data = gdk_pixbuf_get_pixels (dst);
- dst_stride = gdk_pixbuf_get_rowstride (dst);
-
- for (y = 0; y < height; y++)
- {
- src_row = src_data + src_stride * y;
- dst_row = dst_data + dst_stride * y;
- for (x = 0; x < width; x++)
- {
- dst_row[to_plane] = src_row[from_plane];
- src_row += 4;
- dst_row += 4;
- }
- }
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_data (const char *file_data,
- gsize file_len,
- int width,
- int height,
- double scale,
- const char *debug_output_basename,
- GError **error)
-
-{
- const char *r_string = "rgb(255,0,0)";
- const char *g_string = "rgb(0,255,0)";
- char *icon_width_str;
- char *icon_height_str;
- GdkPixbuf *loaded;
- GdkPixbuf *pixbuf = NULL;
- int plane;
- int icon_width, icon_height;
- char *escaped_file_data;
-
- /* Fetch size from the original icon */
- {
- GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
- GdkPixbuf *reference = gdk_pixbuf_new_from_stream (stream, NULL, error);
-
- g_object_unref (stream);
-
- if (!reference)
- return NULL;
-
- icon_width = gdk_pixbuf_get_width (reference);
- icon_height = gdk_pixbuf_get_height (reference);
- g_object_unref (reference);
- }
-
- escaped_file_data = g_base64_encode ((guchar *) file_data, file_len);
- icon_width_str = g_strdup_printf ("%d", icon_width);
- icon_height_str = g_strdup_printf ("%d", icon_height);
-
- if (width == 0)
- width = icon_width * scale;
- if (height == 0)
- height = icon_height * scale;
-
- for (plane = 0; plane < 3; plane++)
- {
- /* Here we render the svg with all colors solid, this should
- * always make the alpha channel the same and it should match
- * the final alpha channel for all possible renderings. We
- * Just use it as-is for final alpha.
- *
- * For the 3 non-fg colors, we render once each with that
- * color as red, and every other color as green. The resulting
- * red will describe the amount of that color is in the
- * opaque part of the color. We store these as the rgb
- * channels, with the color of the fg being implicitly
- * the "rest", as all color fractions should add up to 1.
- */
- loaded = load_symbolic_svg (escaped_file_data, width, height,
- icon_width_str,
- icon_height_str,
- g_string,
- plane == 0 ? r_string : g_string,
- plane == 1 ? r_string : g_string,
- plane == 2 ? r_string : g_string,
- error);
- if (loaded == NULL)
- goto out;
-
- if (debug_output_basename)
- {
- char *filename;
-
- filename = g_strdup_printf ("%s.debug%d.png", debug_output_basename, plane);
- g_print ("Writing %s\n", filename);
- gdk_pixbuf_save (loaded, filename, "png", NULL, NULL);
- g_free (filename);
- }
-
- if (pixbuf == NULL)
- {
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
- gdk_pixbuf_get_width (loaded),
- gdk_pixbuf_get_height (loaded));
- gdk_pixbuf_fill (pixbuf, 0);
- }
-
- if (plane == 0)
- extract_plane (loaded, pixbuf, 3, 3);
-
- extract_plane (loaded, pixbuf, 0, plane);
-
- g_object_unref (loaded);
- }
-
- g_free (escaped_file_data);
-
-out:
- g_free (icon_width_str);
- g_free (icon_height_str);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_resource (const char *path,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GBytes *bytes;
- const char *data;
- gsize size;
- GdkPixbuf *pixbuf;
-
- bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
- if (bytes == NULL)
- return NULL;
-
- data = g_bytes_get_data (bytes, &size);
-
- pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
- g_bytes_unref (bytes);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_path (const char *path,
- int width,
- int height,
- double scale,
- GError **error)
-{
- char *data;
- gsize size;
- GdkPixbuf *pixbuf;
-
- if (!g_file_get_contents (path, &data, &size, error))
- return NULL;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
- g_free (data);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_file (GFile *file,
- int width,
- int height,
- double scale,
- GError **error)
-{
- char *data;
- gsize size;
- GdkPixbuf *pixbuf;
-
- if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
- return NULL;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
- g_free (data);
-
- return pixbuf;
-}
-
-GdkTexture *
-gtk_load_symbolic_texture_from_resource (const char *path)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture;
-
- pixbuf = _gdk_pixbuf_new_from_resource (path, "png", NULL);
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
-
- return texture;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_resource (const char *path,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture = NULL;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
- if (pixbuf)
- {
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
- }
-
- return texture;
-}
-
-GdkTexture *
-gtk_load_symbolic_texture_from_file (GFile *file)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture;
- GInputStream *stream;
-
- stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
- if (stream == NULL)
- return NULL;
-
- pixbuf = _gdk_pixbuf_new_from_stream (stream, "png", NULL, NULL);
- g_object_unref (stream);
- if (pixbuf == NULL)
- return NULL;
-
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
-
- return texture;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_file (GFile *file,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
-
- return texture;
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static const char *
-object_get_id (GObject *object)
-{
- if (GTK_IS_BUILDABLE (object))
- return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
- else
- return g_object_get_data (object, "gtk-builder-id");
-}
-
-void
-do_enumerate (int *argc, const char ***argv)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- int ret;
- GSList *list, *l;
- GObject *object;
- const char *name;
- const char *filename;
-
- filename = (*argv)[1];
-
- builder = gtk_builder_new ();
- ret = gtk_builder_add_from_file (builder, filename, &error);
-
- if (ret == 0)
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-
- list = gtk_builder_get_objects (builder);
- for (l = list; l; l = l->next)
- {
- object = l->data;
- name = object_get_id (object);
- if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
- continue;
-
- g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
- }
- g_slist_free (list);
-
- g_object_unref (builder);
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-
-static void
-set_window_title (GtkWindow *window,
- const char *filename,
- const char *id)
-{
- char *name;
- char *title;
-
- name = g_path_get_basename (filename);
-
- if (id)
- title = g_strdup_printf ("%s in %s", id, name);
- else
- title = g_strdup (name);
-
- gtk_window_set_title (window, title);
-
- g_free (title);
- g_free (name);
-}
-
-static void
-quit_cb (GtkWidget *widget,
- gpointer user_data)
-{
- gboolean *is_done = user_data;
-
- *is_done = TRUE;
-
- g_main_context_wakeup (NULL);
-}
-
-static void
-preview_file (const char *filename,
- const char *id,
- const char *cssfile)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- GObject *object;
- GtkWidget *window;
- gboolean done = FALSE;
-
- if (cssfile)
- {
- GtkCssProvider *provider;
-
- provider = gtk_css_provider_new ();
- gtk_css_provider_load_from_path (provider, cssfile);
-
- gtk_style_context_add_provider_for_display (gdk_display_get_default (),
- GTK_STYLE_PROVIDER (provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- }
-
- builder = gtk_builder_new ();
- if (!gtk_builder_add_from_file (builder, filename, &error))
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-
- object = NULL;
-
- if (id)
- {
- object = gtk_builder_get_object (builder, id);
- }
- else
- {
- GSList *objects, *l;
-
- objects = gtk_builder_get_objects (builder);
- for (l = objects; l; l = l->next)
- {
- GObject *obj = l->data;
-
- if (GTK_IS_WINDOW (obj))
- {
- object = obj;
- break;
- }
- else if (GTK_IS_WIDGET (obj))
- {
- if (object == NULL)
- object = obj;
- }
- }
- g_slist_free (objects);
- }
-
- if (object == NULL)
- {
- if (id)
- g_printerr ("No object with ID '%s' found\n", id);
- else
- g_printerr ("No previewable object found\n");
- exit (1);
- }
-
- if (!GTK_IS_WIDGET (object))
- {
- g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
- exit (1);
- }
-
- if (GTK_IS_WINDOW (object))
- window = GTK_WIDGET (object);
- else
- {
- GtkWidget *widget = GTK_WIDGET (object);
-
- window = gtk_window_new ();
-
- if (GTK_IS_BUILDABLE (object))
- id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
-
- set_window_title (GTK_WINDOW (window), filename, id);
-
- g_object_ref (widget);
- if (gtk_widget_get_parent (widget) != NULL)
- gtk_box_remove (GTK_BOX (gtk_widget_get_parent (widget)), widget);
- gtk_window_set_child (GTK_WINDOW (window), widget);
- g_object_unref (widget);
- }
-
- gtk_window_present (GTK_WINDOW (window));
- g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
-
- while (!done)
- g_main_context_iteration (NULL, TRUE);
-
- g_object_unref (builder);
-}
-
-void
-do_preview (int *argc,
- const char ***argv)
-{
- GOptionContext *context;
- char *id = NULL;
- char *css = NULL;
- char **filenames = NULL;
- const GOptionEntry entries[] = {
- { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
- { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
- { NULL, }
- };
- GError *error = NULL;
-
- context = g_option_context_new (NULL);
- g_option_context_set_help_enabled (context, FALSE);
- g_option_context_add_main_entries (context, entries, NULL);
-
- if (!g_option_context_parse (context, argc, (char ***)argv, &error))
- {
- g_printerr ("%s\n", error->message);
- g_error_free (error);
- exit (1);
- }
-
- g_option_context_free (context);
-
- if (filenames == NULL)
- {
- g_printerr ("No .ui file specified\n");
- exit (1);
- }
-
- if (g_strv_length (filenames) > 1)
- {
- g_printerr ("Can only preview a single .ui file\n");
- exit (1);
- }
-
- preview_file (filenames[0], id, css);
-
- g_strfreev (filenames);
- g_free (id);
- g_free (css);
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-typedef struct Element Element;
-struct Element {
- Element *parent;
- char *element_name;
- char **attribute_names;
- char **attribute_values;
- char *data;
- GList *children;
-
- int line_number;
- int char_number;
-};
-
-static void
-free_element (gpointer data)
-{
- Element *element = data;
- g_list_free_full (element->children, free_element);
- g_free (element->element_name);
- g_strfreev (element->attribute_names);
- g_strfreev (element->attribute_values);
- g_free (element->data);
- g_free (element);
-}
-
-typedef struct {
- Element *root;
- Element *current;
- GString *value;
- GtkBuilder *builder;
- const char *input_filename;
- char *output_filename;
- FILE *output;
- gboolean convert3to4;
- gboolean has_gtk_requires;
-} MyParserData;
-
-static void
-start_element (GMarkupParseContext *context,
- const char *element_name,
- const char **attribute_names,
- const char **attribute_values,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
- Element *elt;
-
- elt = g_new0 (Element, 1);
- elt->parent = data->current;
- elt->element_name = g_strdup (element_name);
- elt->attribute_names = g_strdupv ((char **)attribute_names);
- elt->attribute_values = g_strdupv ((char **)attribute_values);
-
- g_markup_parse_context_get_position (context, &elt->line_number, &elt->char_number);
-
- if (data->current)
- data->current->children = g_list_append (data->current->children, elt);
- data->current = elt;
-
- if (data->root == NULL)
- data->root = elt;
-
- g_string_truncate (data->value, 0);
-}
-
-static void
-end_element (GMarkupParseContext *context,
- const char *element_name,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
-
- data->current->data = g_strdup (data->value->str);
-
- data->current = data->current->parent;
-}
-
-static void
-text (GMarkupParseContext *context,
- const char *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
-
- if (data->value)
- {
- g_string_append_len (data->value, text, text_len);
- return;
- }
-}
-
-static GMarkupParser parser = {
- start_element,
- end_element,
- text,
- NULL,
- NULL
-};
-
-static const char *
-canonical_boolean_value (MyParserData *data,
- const char *string)
-{
- GValue value = G_VALUE_INIT;
- gboolean b = FALSE;
-
- if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
- b = g_value_get_boolean (&value);
-
- return b ? "1" : "0";
-}
-
-typedef enum {
- PROP_KIND_OBJECT,
- PROP_KIND_PACKING,
- PROP_KIND_CELL_PACKING,
- PROP_KIND_LAYOUT
-} PropKind;
-
-static PropKind
-get_prop_kind (Element *element)
-{
- g_assert (g_str_equal (element->element_name, "property"));
-
- if (g_str_equal (element->parent->element_name, "packing"))
- return PROP_KIND_PACKING;
- else if (g_str_equal (element->parent->element_name, "layout"))
- return PROP_KIND_LAYOUT;
- else if (g_str_equal (element->parent->element_name, "cell-packing"))
- return PROP_KIND_CELL_PACKING;
- else
- return PROP_KIND_OBJECT;
-}
-
-/* A number of properties unfortunately can't be omitted even
- * if they are nominally set to their default value. In many
- * cases, this is due to subclasses not overriding the default
- * value from the superclass.
- */
-static gboolean
-needs_explicit_setting (GParamSpec *pspec,
- PropKind kind)
-{
- struct _Prop {
- const char *class;
- const char *property;
- PropKind kind;
- } props[] = {
- { "GtkAboutDialog", "program-name", PROP_KIND_OBJECT },
- { "GtkCalendar", "year", PROP_KIND_OBJECT },
- { "GtkCalendar", "month", PROP_KIND_OBJECT },
- { "GtkCalendar", "day", PROP_KIND_OBJECT },
- { "GtkPlacesSidebar", "show-desktop", PROP_KIND_OBJECT },
- { "GtkRadioButton", "draw-indicator", PROP_KIND_OBJECT },
- { "GtkWidget", "hexpand", PROP_KIND_OBJECT },
- { "GtkWidget", "vexpand", PROP_KIND_OBJECT },
- { "GtkGridLayoutChild", "row", PROP_KIND_LAYOUT },
- { "GtkGridLayoutChild", "column", PROP_KIND_LAYOUT },
- };
- gboolean found;
- int k;
- const char *class_name;
-
- class_name = g_type_name (pspec->owner_type);
-
- found = FALSE;
- for (k = 0; k < G_N_ELEMENTS (props); k++)
- {
- if (strcmp (class_name, props[k].class) == 0 &&
- strcmp (pspec->name, props[k].property) == 0 &&
- kind == props[k].kind)
- {
- found = TRUE;
- break;
- }
- }
-
- return found;
-}
-
-static gboolean
-has_attribute (Element *elt,
- const char *name,
- const char *value)
-{
- int i;
-
- for (i = 0; elt->attribute_names[i]; i++)
- {
- if (strcmp (elt->attribute_names[i], name) == 0 &&
- (value == NULL || strcmp (elt->attribute_values[i], value) == 0))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-is_cdata_property (Element *element)
-{
- if (g_str_equal (element->element_name, "property") &&
- has_attribute (element, "name", "bytes") &&
- element->parent != NULL &&
- g_str_equal (element->parent->element_name, "object") &&
- has_attribute (element->parent, "class", "GtkBuilderListItemFactory"))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-is_pcdata_element (Element *element)
-{
- /* elements that can contain text */
- const char *names[] = {
- "property",
- "attribute",
- "action-widget",
- "pattern",
- "mime-type",
- "col",
- "item",
- "mark",
- NULL,
- };
-
- if (g_str_equal (element->element_name, "property") &&
- (g_strv_contains ((const char * const *)element->attribute_names, "bind-source") ||
- g_strv_contains ((const char * const *)element->attribute_names, "bind_source")))
- return FALSE;
-
- if (g_strv_contains (names, element->element_name))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-is_container_element (Element *element)
-{
- /* elements that just hold a list of things and
- * can be omitted when they have no children
- */
- const char *names[] = {
- "packing",
- "layout",
- "cell-packing",
- "attributes",
- "action-widgets",
- "patterns",
- "mime-types",
- "attributes",
- "row",
- "items",
- NULL
- };
-
- if (g_strv_contains (names, element->element_name))
- return TRUE;
-
- return FALSE;
-}
-
-static void
-canonicalize_key (char *key)
-{
- char *p;
-
- for (p = key; *p != 0; p++)
- {
- char c = *p;
-
- /* We may meet something like AtkObject::accessible-name */
- if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
- continue;
-
- if (c != '-' &&
- (c < '0' || c > '9') &&
- (c < 'A' || c > 'Z') &&
- (c < 'a' || c > 'z'))
- *p = '-';
- }
-}
-
-static struct {
- const char *class;
- const char *layout_manager;
-} layout_managers[] = {
- { "GtkBox", "GtkBoxLayout" },
- { "GtkGrid", "GtkGridLayout" },
- { "GtkFixed", "GtkFixedLayout" },
- { "GtkFileChooserButton", "GtkBinLayout" },
- { "GtkFileChooserWidget", "GtkBinLayout" },
- { "GtkOverlay", "GtkOverlayLayout" }
-};
-
-static GParamSpec *
-get_property_pspec (MyParserData *data,
- const char *class_name,
- const char *property_name,
- PropKind kind)
-{
- GType type;
- GObjectClass *class;
- GParamSpec *pspec;
- char *canonical_name;
-
- type = g_type_from_name (class_name);
- if (type == G_TYPE_INVALID)
- {
- type = gtk_builder_get_type_from_name (data->builder, class_name);
- if (type == G_TYPE_INVALID)
- return NULL;
- }
-
- class = g_type_class_ref (type);
- canonical_name = g_strdup (property_name);
- canonicalize_key (canonical_name);
- switch (kind)
- {
- case PROP_KIND_OBJECT:
- pspec = g_object_class_find_property (class, canonical_name);
- break;
-
- case PROP_KIND_PACKING:
- pspec = NULL;
- break;
-
- case PROP_KIND_CELL_PACKING:
- {
- GObjectClass *cell_class;
-
- /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
- cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
- pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
- g_type_class_unref (cell_class);
- }
- break;
-
- case PROP_KIND_LAYOUT:
- {
- int i;
- const char *layout_manager = NULL;
-
- pspec = NULL;
-
- for (i = 0; i < G_N_ELEMENTS (layout_managers); i++)
- {
- if (g_str_equal (layout_managers[i].class, class_name))
- {
- layout_manager = layout_managers[i].layout_manager;
- break;
- }
- }
-
- if (layout_manager)
- {
- GtkLayoutManagerClass *layout_manager_class;
-
- layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (g_type_class_ref (g_type_from_name (layout_manager)));
- if (layout_manager_class->layout_child_type != G_TYPE_INVALID)
- {
- GObjectClass *layout_child_class;
- layout_child_class = g_type_class_ref (layout_manager_class->layout_child_type);
- pspec = g_object_class_find_property (layout_child_class, canonical_name);
- g_type_class_unref (layout_child_class);
- }
- g_type_class_unref (layout_manager_class);
- }
- }
- break;
-
- default:
- g_assert_not_reached ();
- }
- g_free (canonical_name);
- g_type_class_unref (class);
-
- return pspec;
-}
-
-static const char *get_class_name (Element *element);
-
-static gboolean
-value_is_default (Element *element,
- MyParserData *data,
- GParamSpec *pspec,
- const char *value_string)
-{
- GValue value = { 0, };
- gboolean ret;
- GError *error = NULL;
-
- if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
- return FALSE;
-
- if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_BOXED))
- return FALSE;
-
- if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
- {
- g_printerr (_("%s:%d: Couldn’t parse value for property '%s': %s\n"), data->input_filename, element->line_number, pspec->name, error->message);
- g_error_free (error);
- ret = FALSE;
- }
- else
- {
- /* GtkWidget::visible has a 'smart' default */
- if (pspec->owner_type == GTK_TYPE_WIDGET &&
- g_str_equal (pspec->name, "visible"))
- {
- const char *class_name = get_class_name (element);
- GType type = g_type_from_name (class_name);
- gboolean default_value;
-
- if (g_type_is_a (type, GTK_TYPE_ROOT) ||
- g_type_is_a (type, GTK_TYPE_POPOVER))
- default_value = FALSE;
- else
- default_value = TRUE;
-
- ret = g_value_get_boolean (&value) == default_value;
- }
- else if (pspec->owner_type == GTK_TYPE_WINDOW &&
- (g_str_equal (pspec->name, "default-width") ||
- g_str_equal (pspec->name, "default-height")))
- {
- int default_size;
-
- default_size = g_value_get_int (&value);
- ret = default_size <= 0;
- }
- else
- ret = g_param_value_defaults (pspec, &value);
- }
-
- g_value_reset (&value);
-
- return ret;
-}
-
-static const char *
-get_attribute_value (Element *element,
- const char *name)
-{
- int i;
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (g_str_equal (element->attribute_names[i], name))
- return element->attribute_values[i];
- }
-
- return NULL;
-}
-
-static void
-set_attribute_value (Element *element,
- const char *name,
- const char *value)
-{
- int i;
- int len;
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (g_str_equal (element->attribute_names[i], name))
- {
- g_free (element->attribute_values[i]);
- element->attribute_values[i] = g_strdup (value);
- return;
- }
- }
-
- len = g_strv_length (element->attribute_names);
- element->attribute_names = g_realloc_n (element->attribute_names, len + 2, sizeof (char *));
- element->attribute_values = g_realloc_n (element->attribute_values, len + 2, sizeof (char *));
- element->attribute_names[len] = g_strdup (name);
- element->attribute_values[len] = g_strdup (value);
- element->attribute_names[len + 1] = NULL;
- element->attribute_values[len + 1] = NULL;
-}
-
-static gboolean
-element_is_object_or_template (Element *element)
-{
- return g_str_equal (element->element_name, "object") ||
- g_str_equal (element->element_name, "template");
-}
-
-static const char *
-get_class_name (Element *element)
-{
- Element *parent = element->parent;
-
- if (element_is_object_or_template (element))
- parent = element;
-
- if (g_str_equal (parent->element_name, "packing"))
- parent = parent->parent->parent; /* child - object */
- else if (g_str_equal (parent->element_name, "layout"))
- parent = parent->parent->parent->parent; /* object - child - object */
-
-
- if (g_str_equal (parent->element_name, "object"))
- {
- return get_attribute_value (parent, "class");
- }
- else if (g_str_equal (parent->element_name, "template"))
- {
- if (get_attribute_value (parent, "parent"))
- return get_attribute_value (parent, "parent");
- else
- return get_attribute_value (parent, "class");
- }
-
- return NULL;
-}
-
-static gboolean
-property_is_boolean (Element *element,
- MyParserData *data)
-{
- GParamSpec *pspec = NULL;
- const char *class_name;
- const char *property_name;
- int i;
- PropKind kind;
-
- kind = get_prop_kind (element);
- class_name = get_class_name (element);
- property_name = "";
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (strcmp (element->attribute_names[i], "name") == 0)
- property_name = (const char *)element->attribute_values[i];
- }
-
- if (class_name && property_name)
- pspec = get_property_pspec (data, class_name, property_name, kind);
- if (pspec)
- return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
-
- return FALSE;
-}
-
-static void
-warn_missing_property (Element *element,
- MyParserData *data,
- const char *class_name,
- const char *property_name,
- PropKind kind)
-{
- const char *kind_str[] = { "", "Packing ", "Cell ", "Layout " };
-
- g_printerr (_("%s:%d: %sproperty %s::%s not found\n"),
- data->input_filename, element->line_number, kind_str[kind], class_name, property_name);
-}
-
-static gboolean
-property_can_be_omitted (Element *element,
- MyParserData *data)
-{
- int i;
- gboolean bound;
- gboolean translatable;
- const char *class_name;
- const char *property_name;
- const char *value_string;
- GParamSpec *pspec;
- PropKind kind;
-
- kind = get_prop_kind (element);
- class_name = get_class_name (element);
- property_name = "";
- value_string = element->data;
-
- bound = FALSE;
- translatable = FALSE;
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (strcmp (element->attribute_names[i], "bind-source") == 0 ||
- strcmp (element->attribute_names[i], "bind_source") == 0)
- bound = TRUE;
- else if (strcmp (element->attribute_names[i], "translatable") == 0)
- translatable = TRUE;
- else if (strcmp (element->attribute_names[i], "name") == 0)
- property_name = (const char *)element->attribute_values[i];
- }
-
- if (translatable)
- return FALSE;
-
- if (bound)
- return FALSE;
-
- pspec = get_property_pspec (data, class_name, property_name, kind);
- if (pspec == NULL)
- {
- warn_missing_property (element, data, class_name, property_name, kind);
- return FALSE;
- }
-
- if (needs_explicit_setting (pspec, kind))
- return FALSE;
-
- return value_is_default (element, data, pspec, value_string);
-}
-
-static gboolean
-property_has_been_removed (Element *element,
- MyParserData *data)
-{
- const char *class_name;
- const char *property_name;
- struct _Prop {
- const char *class;
- const char *property;
- PropKind kind;
- } props[] = {
- { "GtkActionBar", "position", PROP_KIND_PACKING },
- { "GtkButtonBox", "secondary", PROP_KIND_PACKING },
- { "GtkButtonBox", "non-homogeneous", PROP_KIND_PACKING },
- { "GtkBox", "position", PROP_KIND_PACKING },
- { "GtkBox", "pack-type", PROP_KIND_PACKING },
- { "GtkHeaderBar", "position", PROP_KIND_PACKING },
- { "GtkPopoverMenu", "position",PROP_KIND_PACKING },
- { "GtkCheckButton", "draw-indicator", PROP_KIND_OBJECT },
- };
- char *canonical_name;
- gboolean found;
- int i, k;
- PropKind kind;
-
- kind = get_prop_kind (element);
-
- class_name = get_class_name (element);
- property_name = "";
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (strcmp (element->attribute_names[i], "name") == 0)
- property_name = (const char *)element->attribute_values[i];
- }
-
- canonical_name = g_strdup (property_name);
- g_strdelimit (canonical_name, "_", '-');
-
- found = FALSE;
- for (k = 0; k < G_N_ELEMENTS (props); k++)
- {
- if (strcmp (class_name, props[k].class) == 0 &&
- strcmp (canonical_name, props[k].property) == 0 &&
- kind == props[k].kind)
- {
- found = TRUE;
- break;
- }
- }
-
- g_free (canonical_name);
-
- return found;
-}
-
-static void
-maybe_rename_property (Element *element, MyParserData *data)
-{
- const char *class_name;
- const char *property_name;
- struct _Prop {
- const char *class;
- const char *property;
- GType type;
- PropKind kind;
- const char *new_name;
- const char *alt_names[3];
- } props[] = {
- /* the "replacement" property is placed *after* the "added" properties */
- { "GtkPopover", "modal", GTK_TYPE_POPOVER, PROP_KIND_OBJECT, "autohide", { NULL, NULL, NULL } },
- { "GtkWidget", "expand", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "vexpand", { "hexpand", NULL, NULL } },
- { "GtkWidget", "margin", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-bottom", { "margin-start", "margin-end", "margin-top" } },
- { "GtkWidget", "margin-left", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-start", { NULL, NULL, NULL } },
- { "GtkWidget", "margin-right", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-end", { NULL, NULL, NULL } },
- { "GtkHeaderBar", "show-close-button", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "show-title-buttons", { NULL, NULL, NULL } },
- { "GtkHeaderBar", "custom-title", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "title-widget", { NULL, NULL, NULL } },
- { "GtkStack", "homogeneous", GTK_TYPE_STACK, PROP_KIND_OBJECT, "hhomogeneous", { "vhomogeneous", NULL, NULL } },
- { "GtkImage", "pixbuf", GTK_TYPE_IMAGE, PROP_KIND_OBJECT, "file", { NULL, NULL, NULL } },
- };
- int i, k, l;
- PropKind kind;
- int prop_name_index = 0;
- GType type;
- char *canonical_name;
-
- kind = get_prop_kind (element);
-
- class_name = get_class_name (element);
- property_name = NULL;
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- if (strcmp (element->attribute_names[i], "name") == 0)
- {
- prop_name_index = i;
- property_name = (const char *)element->attribute_values[i];
- }
- }
-
- if (property_name == NULL)
- return;
-
- type = g_type_from_name (class_name);
-
- canonical_name = g_strdup (property_name);
- g_strdelimit (canonical_name, "_", '-');
-
- for (k = 0; k < G_N_ELEMENTS (props); k++)
- {
- if (g_type_is_a (type, props[k].type) &&
- strcmp (canonical_name, props[k].property) == 0 &&
- kind == props[k].kind)
- {
- g_free (element->attribute_values[prop_name_index]);
- element->attribute_values[prop_name_index] = g_strdup (props[k].new_name);
- for (l = 0; l < 3 && props[k].alt_names[l]; l++)
- {
- Element *elt;
- GList *sibling;
-
- elt = g_new0 (Element, 1);
- elt->parent = element->parent;
- elt->element_name = g_strdup (element->element_name);
- elt->attribute_names = g_strdupv ((char **) element->attribute_names);
- elt->attribute_values = g_strdupv ((char **) element->attribute_values);
- elt->data = g_strdup (element->data);
-
- g_free (elt->attribute_values[prop_name_index]);
- elt->attribute_values[prop_name_index] = g_strdup (props[k].alt_names[l]);
-
- sibling = g_list_find (element->parent->children, element);
- element->parent->children = g_list_insert_before (element->parent->children,
- sibling,
- elt);
- }
- break;
- }
- }
-
- g_free (canonical_name);
-}
-
-static Element *
-rewrite_stack_child (Element *child, MyParserData *data)
-{
- Element *object = NULL;
- Element *packing = NULL;
- Element *new_object;
- Element *prop;
- GList *l;
-
- if (!g_str_equal (child->element_name, "child"))
- return child;
-
- for (l = child->children; l; l = l->next)
- {
- Element *elt = l->data;
- if (g_str_equal (elt->element_name, "object"))
- object = elt;
- else if (g_str_equal (elt->element_name, "packing"))
- packing = elt;
- }
-
- if (!packing)
- return child;
-
- new_object = g_new0 (Element, 1);
- new_object->element_name = g_strdup ("object");
- new_object->attribute_names = g_new0 (char *, 2);
- new_object->attribute_names[0] = g_strdup ("class");
- new_object->attribute_values = g_new0 (char *, 2);
- new_object->attribute_values[0] = g_strdup ("GtkStackPage");
- new_object->children = packing->children;
- new_object->parent = child;
- packing->children = NULL;
-
- prop = g_new0 (Element, 1);
- prop->element_name = g_strdup ("property");
- prop->attribute_names = g_new0 (char *, 2);
- prop->attribute_names[0] = g_strdup ("name");
- prop->attribute_values = g_new0 (char *, 2);
- prop->attribute_values[0] = g_strdup ("child");
- prop->children = g_list_append (prop->children, object);
- prop->parent = new_object;
- new_object->children = g_list_append (new_object->children, prop);
-
- g_list_free (child->children);
- child->children = g_list_append (NULL, new_object);
-
- return child;
-}
-
-static void
-rewrite_stack (Element *element,
- MyParserData *data)
-{
- GList *l, *new_children;
-
- new_children = NULL;
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- new_children = g_list_append (new_children, rewrite_stack_child (child, data));
- }
-
- g_list_free (element->children);
- element->children = new_children;
-}
-
-static Element *
-rewrite_assistant_child (Element *child, MyParserData *data)
-{
- Element *object = NULL;
- Element *packing = NULL;
- Element *new_object;
- Element *prop;
- GList *l;
-
- if (!g_str_equal (child->element_name, "child"))
- return child;
-
- for (l = child->children; l; l = l->next)
- {
- Element *elt = l->data;
- if (g_str_equal (elt->element_name, "object"))
- object = elt;
- else if (g_str_equal (elt->element_name, "packing"))
- packing = elt;
- }
-
- if (!packing)
- return child;
-
- new_object = g_new0 (Element, 1);
- new_object->element_name = g_strdup ("object");
- new_object->attribute_names = g_new0 (char *, 2);
- new_object->attribute_names[0] = g_strdup ("class");
- new_object->attribute_values = g_new0 (char *, 2);
- new_object->attribute_values[0] = g_strdup ("GtkAssistantPage");
- new_object->children = packing->children;
- new_object->parent = child;
- packing->children = NULL;
-
- prop = g_new0 (Element, 1);
- prop->element_name = g_strdup ("property");
- prop->attribute_names = g_new0 (char *, 2);
- prop->attribute_names[0] = g_strdup ("name");
- prop->attribute_values = g_new0 (char *, 2);
- prop->attribute_values[0] = g_strdup ("child");
- prop->children = g_list_append (prop->children, object);
- prop->parent = new_object;
- new_object->children = g_list_append (new_object->children, prop);
-
- g_list_free (child->children);
- child->children = g_list_append (NULL, new_object);
-
- return child;
-}
-
-static void
-rewrite_assistant (Element *element,
- MyParserData *data)
-{
- GList *l, *new_children;
-
- new_children = NULL;
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- new_children = g_list_append (new_children, rewrite_assistant_child (child, data));
- }
-
- g_list_free (element->children);
- element->children = new_children;
-}
-
-static Element *
-rewrite_notebook_page (Element *child, Element *tab, MyParserData *data)
-{
- Element *object = NULL;
- Element *tab_obj = NULL;
- Element *packing = NULL;
- Element *new_object;
- Element *prop;
- GList *l;
-
- if (!g_str_equal (child->element_name, "child"))
- return child;
-
- if (has_attribute (child, "type", "tab") ||
- has_attribute (child, "type", "action-start") ||
- has_attribute (child, "type", "action-end"))
- return child;
-
- for (l = child->children; l; l = l->next)
- {
- Element *elt = l->data;
- if (g_str_equal (elt->element_name, "object"))
- object = elt;
- else if (g_str_equal (elt->element_name, "packing"))
- packing = elt;
- }
-
- if (!packing && !tab)
- return child;
-
- if (tab)
- {
- for (l = tab->children; l; l = l->next)
- {
- Element *elt = l->data;
- if (g_str_equal (elt->element_name, "object"))
- tab_obj = elt;
- }
- }
-
- new_object = g_new0 (Element, 1);
- new_object->element_name = g_strdup ("object");
- new_object->attribute_names = g_new0 (char *, 2);
- new_object->attribute_names[0] = g_strdup ("class");
- new_object->attribute_values = g_new0 (char *, 2);
- new_object->attribute_values[0] = g_strdup ("GtkNotebookPage");
- new_object->parent = child;
- if (packing)
- {
- new_object->children = packing->children;
- packing->children = NULL;
- }
-
- prop = g_new0 (Element, 1);
- prop->element_name = g_strdup ("property");
- prop->attribute_names = g_new0 (char *, 2);
- prop->attribute_names[0] = g_strdup ("name");
- prop->attribute_values = g_new0 (char *, 2);
- prop->attribute_values[0] = g_strdup ("child");
- prop->children = g_list_append (prop->children, object);
- prop->parent = new_object;
- new_object->children = g_list_append (new_object->children, prop);
-
- if (tab_obj)
- {
- prop = g_new0 (Element, 1);
- prop->element_name = g_strdup ("property");
- prop->attribute_names = g_new0 (char *, 2);
- prop->attribute_names[0] = g_strdup ("name");
- prop->attribute_values = g_new0 (char *, 2);
- prop->attribute_values[0] = g_strdup ("tab");
- prop->children = g_list_append (prop->children, tab_obj);
- prop->parent = new_object;
- new_object->children = g_list_append (new_object->children, prop);
- }
-
- g_list_free (child->children);
- child->children = g_list_append (NULL, new_object);
-
- return child;
-}
-
-static void
-rewrite_notebook (Element *element,
- MyParserData *data)
-{
- GList *l, *new_children;
-
- new_children = NULL;
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- Element *tab = l->next ? l->next->data : NULL;
-
- if (tab && has_attribute (tab, "type", "tab"))
- {
- new_children = g_list_append (new_children, rewrite_notebook_page (child, tab, data));
- l = l->next; /* skip the tab */
- }
- else
- new_children = g_list_append (new_children, rewrite_notebook_page (child, NULL, data));
- }
-
- g_list_free (element->children);
- element->children = new_children;
-}
-
-static void
-rewrite_pack_type_child (Element *element,
- MyParserData *data)
-{
- Element *pack_type = NULL;
- GList *l, *ll;
-
- if (!g_str_equal (element->element_name, "child"))
- return;
-
- for (l = element->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "packing"))
- {
- for (ll = elt->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "property") &&
- has_attribute (elt2, "name", "pack-type"))
- {
- pack_type = elt2;
- elt->children = g_list_remove (elt->children, pack_type);
- if (elt->children == NULL)
- {
- element->children = g_list_remove (element->children, elt);
- free_element (elt);
- }
- break;
- }
- }
- }
-
- if (pack_type)
- break;
- }
-
- if (pack_type)
- {
- char **attnames = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
- char **attvalues = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
- int i;
-
- for (i = 0; element->attribute_names[i]; i++)
- {
- attnames[i] = g_strdup (element->attribute_names[i]);
- attvalues[i] = g_strdup (element->attribute_values[i]);
- }
-
- attnames[i] = g_strdup ("type");
- attvalues[i] = g_strdup (pack_type->data);
-
- g_strfreev (element->attribute_names);
- g_strfreev (element->attribute_values);
-
- element->attribute_names = attnames;
- element->attribute_values = attvalues;
-
- free_element (pack_type);
- }
-}
-
-static void
-rewrite_pack_type (Element *element,
- MyParserData *data)
-{
- GList *l;
-
- for (l = element->children; l; l = l->next)
- {
- Element *elt = l->data;
- if (g_str_equal (elt->element_name, "child"))
- rewrite_pack_type_child (elt, data);
- }
-}
-
-static void
-rewrite_paned_child (Element *element,
- MyParserData *data,
- Element *child,
- const char *suffix)
-{
- Element *resize = NULL;
- Element *shrink = NULL;
- GList *l, *ll;
-
- for (l = child->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "packing"))
- {
- for (ll = elt->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "property") &&
- has_attribute (elt2, "name", "resize"))
- resize = elt2;
- else if (g_str_equal (elt2->element_name, "property") &&
- has_attribute (elt2, "name", "shrink"))
- shrink = elt2;
- }
- if (resize)
- elt->children = g_list_remove (elt->children, resize);
- if (shrink)
- elt->children = g_list_remove (elt->children, shrink);
- if (elt->children == NULL)
- {
- child->children = g_list_remove (child->children, elt);
- free_element (elt);
- }
- }
-
- if (resize || shrink)
- break;
- }
-
- if (resize)
- {
- Element *elt;
-
- elt = g_new0 (Element, 1);
- elt->parent = element;
- elt->element_name = g_strdup ("property");
- elt->attribute_names = g_new0 (char *, 2);
- elt->attribute_names[0] = g_strdup ("name");
- elt->attribute_values = g_new0 (char *, 2);
- elt->attribute_values[0] = g_strconcat ("resize-", suffix, NULL);
- elt->data = g_strdup (resize->data);
-
- element->children = g_list_prepend (element->children, elt);
-
- free_element (resize);
- }
-
- if (shrink)
- {
- Element *elt;
-
- elt = g_new0 (Element, 1);
- elt->parent = element;
- elt->element_name = g_strdup ("property");
- elt->attribute_names = g_new0 (char *, 2);
- elt->attribute_names[0] = g_strdup ("name");
- elt->attribute_values = g_new0 (char *, 2);
- elt->attribute_values[0] = g_strconcat ("shrink-", suffix, NULL);
- elt->data = g_strdup (shrink->data);
-
- element->children = g_list_prepend (element->children, elt);
-
- free_element (shrink);
- }
-}
-
-static void
-rewrite_paned (Element *element,
- MyParserData *data)
-{
- Element *child1 = NULL;
- Element *child2 = NULL;
- GList *l;
-
- for (l = element->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "child"))
- {
- if (child1 == NULL)
- child1 = elt;
- else if (child2 == NULL)
- child2 = elt;
- else
- break;
- }
- }
-
- if (child1)
- rewrite_paned_child (element, data, child1, "start-child");
-
- if (child2)
- rewrite_paned_child (element, data, child2, "end-child");
-}
-
-static void
-rewrite_dialog (Element *element,
- MyParserData *data)
-{
- Element *content_area = NULL;
- Element *vbox = NULL;
- Element *action_area = NULL;
- GList *l;
-
- for (l = element->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "child") &&
- g_strcmp0 (get_attribute_value (elt, "internal-child"), "vbox") == 0)
- {
- content_area = elt;
- break;
- }
- }
-
- if (!content_area || !content_area->children)
- return;
-
- vbox = content_area->children->data;
-
- for (l = vbox->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "child") &&
- g_strcmp0 (get_attribute_value (elt, "internal-child"), "action_area") == 0)
- {
- action_area = elt;
- break;
- }
- }
-
- if (!action_area)
- return;
-
- set_attribute_value (content_area, "internal-child", "content_area");
- vbox->children = g_list_remove (vbox->children, action_area);
- action_area->parent = element;
- element->children = g_list_append (element->children, action_area);
-
- for (l = action_area->children; l; l = l->next)
- {
- Element *elt = l->data;
-
- if (g_str_equal (elt->element_name, "packing"))
- {
- action_area->children = g_list_remove (action_area->children, elt);
- free_element (elt);
- break;
- }
- }
-
-}
-
-static void
-rewrite_grid_layout_prop (Element *element,
- const char *attr_name,
- const char *old_value,
- const char *new_value)
-{
- if (g_str_equal (element->element_name, "property"))
- {
- char *canonical_name;
-
- canonical_name = g_strdup (old_value);
- g_strdelimit (canonical_name, "_", '-');
-
- if (has_attribute (element, attr_name, old_value) ||
- has_attribute (element, attr_name, canonical_name))
- {
- set_attribute_value (element, attr_name, new_value);
- }
-
- g_free (canonical_name);
- }
-}
-
-static void
-rewrite_grid_layout (Element *element,
- MyParserData *data)
-{
- struct _Prop {
- const char *attr_name;
- const char *old_value;
- const char *new_value;
- } props[] = {
- { "name", "left_attach", "column", },
- { "name", "top_attach", "row", },
- { "name", "width", "column-span", },
- { "name", "height", "row-span", },
- };
- GList *l, *ll;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "child"))
- {
- Element *object = NULL;
- Element *packing = NULL;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "object"))
- object = elt2;
-
- if (g_str_equal (elt2->element_name, "packing"))
- packing = elt2;
- }
-
- if (object && packing)
- {
- int i;
-
- child->children = g_list_remove (child->children, packing);
-
- g_free (packing->element_name);
- packing->element_name = g_strdup ("layout");
-
- packing->parent = object;
- object->children = g_list_append (object->children, packing);
-
- for (ll = packing->children; ll; ll = ll->next)
- {
- Element *elt = ll->data;
-
- for (i = 0; i < G_N_ELEMENTS (props); i++)
- rewrite_grid_layout_prop (elt,
- props[i].attr_name,
- props[i].old_value,
- props[i].new_value);
- }
- }
- }
- }
-}
-
-static Element *
-add_element (Element *parent,
- const char *element_name)
-{
- Element *child;
-
- child = g_new0 (Element, 1);
- child->parent = parent;
- child->element_name = g_strdup (element_name);
- child->attribute_names = g_new0 (char *, 1);
- child->attribute_values = g_new0 (char *, 1);
- parent->children = g_list_prepend (parent->children, child);
-
- return child;
-}
-
-static Element *
-write_box_prop (Element *element,
- Element *parent,
- const char *name,
- const char *value)
-{
-
- if (element)
- g_free (element->data);
- else
- {
- element = add_element (parent, "property");
- set_attribute_value (element, "name", name);
- }
- element->data = g_strdup (value);
-
- return element;
-}
-
-static void
-rewrite_box (Element *element,
- MyParserData *data)
-{
- GList *l, *ll;
- GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
-
- if (g_str_equal (get_class_name (element), "GtkVBox"))
- write_box_prop (NULL, element, "orientation", "vertical");
-
- if (!g_str_equal (get_class_name (element), "GtkBox"))
- set_attribute_value (element, "class", "GtkBox");
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "property"))
- {
- if (has_attribute (child, "name", "orientation"))
- {
- GValue value = G_VALUE_INIT;
-
- if (gtk_builder_value_from_string_type (data->builder,
- GTK_TYPE_ORIENTATION,
- child->data,
- &value,
- NULL))
- orientation = g_value_get_enum (&value);
- }
- }
- }
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- if (g_str_equal (child->element_name, "child"))
- {
- Element *object = NULL;
- Element *packing = NULL;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "object"))
- object = elt2;
-
- if (g_str_equal (elt2->element_name, "packing"))
- packing = elt2;
- }
-
- if (object && packing)
- {
- Element *halign = NULL;
- Element *hexpand = NULL;
- Element *valign = NULL;
- Element *vexpand = NULL;
-
- gboolean expand = FALSE;
- gboolean fill = TRUE;
-
- for (ll = object->children; ll; ll = ll->next)
- {
- Element *elt = ll->data;
- if (g_str_equal (elt->element_name, "property"))
- {
- if (has_attribute (elt, "name", "halign"))
- halign = elt;
- else if (has_attribute (elt, "name", "hexpand"))
- hexpand = elt;
- else if (has_attribute (elt, "name", "valign"))
- valign = elt;
- else if (has_attribute (elt, "name", "vexpand"))
- vexpand = elt;
- }
- }
-
- for (ll = packing->children; ll; ll = ll->next)
- {
- Element *elt = ll->data;
-
- if (has_attribute (elt, "name", "expand"))
- {
- GValue value = G_VALUE_INIT;
-
- if (gtk_builder_value_from_string_type (data->builder,
- G_TYPE_BOOLEAN,
- elt->data,
- &value,
- NULL))
- expand = g_value_get_boolean (&value);
- }
-
- if (has_attribute (elt, "name", "fill"))
- {
- GValue value = G_VALUE_INIT;
-
- if (gtk_builder_value_from_string_type (data->builder,
- G_TYPE_BOOLEAN,
- elt->data,
- &value,
- NULL))
- fill = g_value_get_boolean (&value);
- }
- }
-
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- {
- if (expand)
- hexpand = write_box_prop (hexpand, object, "hexpand", "1");
- if (!fill)
- halign = write_box_prop (halign, object, "halign", "center");
- }
- else if (orientation == GTK_ORIENTATION_VERTICAL)
- {
- if (expand)
- vexpand = write_box_prop (vexpand, object, "vexpand", "1");
- if (!fill)
- valign = write_box_prop (valign, object, "valign", "center");
- }
-
- child->children = g_list_remove (child->children, packing);
- free_element (packing);
- }
- }
- }
-}
-
-static void
-rewrite_bin_child (Element *element,
- MyParserData *data)
-{
- GList *l, *ll;
- const char *class_name;
- GType type;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- Element *object = NULL;
-
- if (!g_str_equal (child->element_name, "child") ||
- has_attribute (child, "type", NULL))
- continue;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elem = ll->data;
-
- if (!g_str_equal (elem->element_name, "object"))
- continue;
-
- class_name = get_attribute_value (elem, "class");
- if (!class_name)
- continue;
-
- type = g_type_from_name (class_name);
- if (!g_type_is_a (type, GTK_TYPE_WIDGET))
- continue;
-
- object = elem;
- }
-
- if (object)
- {
- g_free (child->element_name);
- g_strfreev (child->attribute_names);
- g_strfreev (child->attribute_values);
- child->element_name = g_strdup ("property");
- child->attribute_names = g_new0 (char *, 2);
- child->attribute_names[0] = g_strdup ("name");
- child->attribute_values = g_new0 (char *, 2);
- child->attribute_values[0] = g_strdup ("child");
- break;
- }
- }
-}
-
-static gboolean
-remove_boolean_prop (Element *element,
- MyParserData *data,
- const char *prop_name,
- gboolean *value)
-{
- GList *l;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "property") &&
- has_attribute (child, "name", prop_name))
- {
- *value = strcmp (canonical_boolean_value (data, child->data), "1") == 0;
- element->children = g_list_remove (element->children, child);
- free_element (child);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static void
-rewrite_radio_button (Element *element,
- MyParserData *data)
-{
- gboolean draw_indicator = TRUE;
-
- if (!remove_boolean_prop (element, data, "draw-indicator", &draw_indicator))
- remove_boolean_prop (element, data, "draw_indicator", &draw_indicator);
-
- if (draw_indicator)
- set_attribute_value (element, "class", "GtkCheckButton");
- else
- set_attribute_value (element, "class", "GtkToggleButton");
-
-}
-
-static gboolean
-has_prop (Element *element,
- MyParserData *data,
- const char *prop_name)
-{
- GList *l;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "property") &&
- has_attribute (child, "name", prop_name))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-rewrite_scale (Element *element,
- MyParserData *data)
-{
- if (!has_prop (element, data, "draw-value") &&
- !has_prop (element, data, "draw_value"))
- {
- Element *child;
- child = add_element (element, "property");
- set_attribute_value (child, "name", "draw-value");
- child->data = g_strdup ("1");
- }
-}
-
-static void
-rewrite_requires (Element *element,
- MyParserData *data)
-{
- if (has_attribute (element, "lib", "gtk+"))
- {
- set_attribute_value (element, "lib", "gtk");
- set_attribute_value (element, "version", "4.0");
- }
-}
-
-static void
-rewrite_overlay (Element *element,
- MyParserData *data)
-{
- GList *l, *ll;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "child"))
- {
- Element *object = NULL;
- Element *packing = NULL;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "object"))
- object = elt2;
-
- if (g_str_equal (elt2->element_name, "packing"))
- packing = elt2;
- }
-
- if (object && packing)
- {
- child->children = g_list_remove (child->children, packing);
-
- for (ll = packing->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "property") &&
- (has_attribute (elt2, "name", "pass-through") ||
- has_attribute (elt2, "name", "pass_through")))
- {
- const char *b = canonical_boolean_value (data, elt2->data);
- if (g_str_equal (b, "1"))
- {
- Element *new_prop;
-
- new_prop = add_element (object, "property");
- set_attribute_value (new_prop, "name", "can-target");
- new_prop->data = g_strdup ("0");
- }
- break;
- }
- }
-
- free_element (packing);
- }
- }
- }
-}
-
-static void
-rewrite_toolbar (Element *element,
- MyParserData *data)
-{
- GList *l, *ll;
- Element *style = NULL;
-
- set_attribute_value (element, "class", "GtkBox");
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "property") &&
- (has_attribute (child, "name", "toolbar_style") ||
- has_attribute (child, "name", "toolbar-style")))
- {
- element->children = g_list_remove (element->children, child);
- free_element (child);
- break;
- }
- }
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- Element *object = NULL;
- Element *packing = NULL;
-
- if (g_str_equal (child->element_name, "style"))
- style = child;
-
- if (!g_str_equal (child->element_name, "child"))
- continue;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "object"))
- object = elt2;
-
- if (g_str_equal (elt2->element_name, "packing"))
- packing = elt2;
- }
-
- if (object)
- {
- const char *class_name;
-
- class_name = get_class_name (object);
-
- if (g_str_equal (class_name, "GtkToolButton"))
- {
- set_attribute_value (object, "class", "GtkButton");
- }
- else if (g_str_equal (class_name, "GtkToggleToolButton") ||
- g_str_equal (class_name, "GtkRadioToolButton"))
- {
- set_attribute_value (object, "class", "GtkToggleButton");
- }
- else if (g_str_equal (class_name, "GtkSeparatorToolItem"))
- {
- Element *prop;
-
- set_attribute_value (object, "class", "GtkSeparator");
- prop = add_element (object, "property");
- set_attribute_value (prop, "name", "orientation");
- prop->data = g_strdup ("vertical");
- }
- }
-
- if (packing)
- child->children = g_list_remove (child->children, packing);
- }
-
- if (!style)
- style = add_element (element, "style");
-
- set_attribute_value (add_element (style, "class"), "name", "toolbar");
-}
-
-static void
-rewrite_fixed (Element *element,
- MyParserData *data)
-{
- GList *l, *ll;
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
-
- if (g_str_equal (child->element_name, "child"))
- {
- Element *object = NULL;
- Element *packing = NULL;
-
- for (ll = child->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
-
- if (g_str_equal (elt2->element_name, "object"))
- object = elt2;
-
- if (g_str_equal (elt2->element_name, "packing"))
- packing = elt2;
- }
-
- if (object && packing)
- {
- int x = 0;
- int y = 0;
- Element *layout;
- Element *new_prop;
- GskTransform *transform;
-
- for (ll = packing->children; ll; ll = ll->next)
- {
- Element *elt2 = ll->data;
- GValue value = G_VALUE_INIT;
-
- if (has_attribute (elt2, "name", "x"))
- {
- if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
- x = g_value_get_int (&value);
- }
- else if (has_attribute (elt2, "name", "y"))
- {
- if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
- y = g_value_get_int (&value);
- }
- }
-
- child->children = g_list_remove (child->children, packing);
- free_element (packing);
-
- layout = add_element (object, "layout");
- new_prop = add_element (layout, "property");
- set_attribute_value (new_prop, "name", "transform");
-
- transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y));
- new_prop->data = gsk_transform_to_string (transform);
- gsk_transform_unref (transform);
- }
- }
- }
-}
-
-/* returns TRUE to remove the element from the parent */
-static gboolean
-simplify_element (Element *element,
- MyParserData *data)
-{
- GList *l;
-
- if (!is_pcdata_element (element))
- g_clear_pointer (&element->data, g_free);
- else if (g_str_equal (element->element_name, "property") &&
- property_is_boolean (element, data))
- {
- const char *b = canonical_boolean_value (data, element->data);
- g_free (element->data);
- element->data = g_strdup (b);
- }
-
- l = element->children;
- while (l)
- {
- GList *next = l->next;
- Element *child = l->data;
- if (simplify_element (child, data))
- {
- element->children = g_list_remove (element->children, child);
- free_element (child);
- }
- l = next;
- }
-
- if (is_container_element (element) && element->children == NULL)
- return TRUE;
-
- if (g_str_equal (element->element_name, "property") &&
- property_can_be_omitted (element, data))
- return TRUE;
-
- if (g_str_equal (element->element_name, "binding"))
- {
- const char *property_name = get_attribute_value (element, "name");
- const char *class_name = get_class_name (element);
- if (!get_property_pspec (data, class_name, property_name, PROP_KIND_OBJECT))
- warn_missing_property (element, data, class_name, property_name, PROP_KIND_OBJECT);
- }
-
- return FALSE;
-}
-
-static void
-simplify_tree (MyParserData *data)
-{
- simplify_element (data->root, data);
-}
-
-static gboolean
-rewrite_element (Element *element,
- MyParserData *data)
-{
- GList *l;
-
- l = element->children;
- while (l)
- {
- GList *next = l->next;
- Element *child = l->data;
- if (rewrite_element (child, data))
- {
- element->children = g_list_remove (element->children, child);
- free_element (child);
- }
- l = next;
- }
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkStack"))
- rewrite_stack (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkAssistant"))
- rewrite_assistant (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkNotebook"))
- rewrite_notebook (element, data);
-
- if (element_is_object_or_template (element) &&
- (g_str_equal (get_class_name (element), "GtkActionBar") ||
- g_str_equal (get_class_name (element), "GtkHeaderBar")))
- rewrite_pack_type (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkToolbar"))
- rewrite_toolbar (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkPaned"))
- rewrite_paned (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkDialog"))
- rewrite_dialog (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkOverlay"))
- rewrite_overlay (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkGrid"))
- rewrite_grid_layout (element, data);
-
- if (element_is_object_or_template (element) &&
- (g_str_equal (get_class_name (element), "GtkHBox") ||
- g_str_equal (get_class_name (element), "GtkVBox") ||
- g_str_equal (get_class_name (element), "GtkBox")))
- rewrite_box (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkFixed"))
- rewrite_fixed (element, data);
-
- if (element_is_object_or_template (element) &&
- (g_str_equal (get_class_name (element), "GtkAspectFrame") ||
- g_str_equal (get_class_name (element), "GtkComboBox") ||
- g_str_equal (get_class_name (element), "GtkComboBoxText") ||
- g_str_equal (get_class_name (element), "GtkFlowBoxChild") ||
- g_str_equal (get_class_name (element), "GtkFrame") ||
- g_str_equal (get_class_name (element), "GtkListBoxRow") ||
- g_str_equal (get_class_name (element), "GtkOverlay") ||
- g_str_equal (get_class_name (element), "GtkPopover") ||
- g_str_equal (get_class_name (element), "GtkPopoverMenu") ||
- g_str_equal (get_class_name (element), "GtkRevealer") ||
- g_str_equal (get_class_name (element), "GtkScrolledWindow") ||
- g_str_equal (get_class_name (element), "GtkSearchBar") ||
- g_str_equal (get_class_name (element), "GtkViewport") ||
- g_str_equal (get_class_name (element), "GtkWindow")))
- rewrite_bin_child (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkRadioButton"))
- rewrite_radio_button (element, data);
-
- if (element_is_object_or_template (element) &&
- g_str_equal (get_class_name (element), "GtkScale"))
- rewrite_scale (element, data);
-
- if (g_str_equal (element->element_name, "property"))
- maybe_rename_property (element, data);
-
- if (g_str_equal (element->element_name, "property") &&
- property_has_been_removed (element, data))
- return TRUE;
-
- if (g_str_equal (element->element_name, "requires"))
- rewrite_requires (element, data);
-
- return FALSE;
-}
-
-static void
-rewrite_tree (MyParserData *data)
-{
- rewrite_element (data->root, data);
-}
-
-/* For properties which have changed their default
- * value between 3 and 4, we make sure that their
- * old default value is present in the tree before
- * simplifying it.
- *
- * So far, this is just GtkWidget::visible,
- * changing its default from 0 to 1.
- */
-static void
-add_old_default_properties (Element *element,
- MyParserData *data)
-{
- const char *class_name;
- GType type;
-
- if (!g_str_equal (element->element_name, "object"))
- return;
-
- class_name = get_class_name (element);
- type = g_type_from_name (class_name);
- if (g_type_is_a (type, GTK_TYPE_WIDGET))
- {
- GList *l;
- gboolean has_visible = FALSE;
-
- for (l = element->children; l; l = l->next)
- {
- Element *prop = l->data;
- const char *name = get_attribute_value (prop, "name");
-
- if (g_str_equal (prop->element_name, "property") &&
- g_str_equal (name, "visible"))
- has_visible = TRUE;
- }
-
- if (!has_visible)
- {
- Element *new_prop;
-
- new_prop = add_element (element, "property");
- set_attribute_value (new_prop, "name", "visible");
- new_prop->data = g_strdup ("0");
- }
- }
-}
-
-static void
-enhance_element (Element *element,
- MyParserData *data)
-{
- GList *l;
-
- if (strcmp (element->element_name, "requires") == 0 &&
- has_attribute (element, "lib", "gtk+"))
- {
- data->has_gtk_requires = TRUE;
- }
-
- add_old_default_properties (element, data);
-
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- enhance_element (child, data);
- }
-
- if (element == data->root && !data->has_gtk_requires)
- {
- Element *requires = add_element (element, "requires");
- set_attribute_value (requires, "lib", "gtk+");
- set_attribute_value (requires, "version", "3.0");
- }
-}
-
-static void
-enhance_tree (MyParserData *data)
-{
- enhance_element (data->root, data);
-}
-
-static void
-dump_element (Element *element,
- FILE *output,
- int indent)
-{
- g_fprintf (output, "%*s<%s", indent, "", element->element_name);
- if (element->attribute_names[0])
- {
- int i;
- for (i = 0; element->attribute_names[i]; i++)
- {
- char *escaped = g_markup_escape_text (element->attribute_values[i], -1);
- g_fprintf (output, " %s=\"%s\"", element->attribute_names[i], escaped);
- g_free (escaped);
- }
- }
- if (element->children || element->data)
- {
- g_fprintf (output, ">");
-
- if (element->children)
- {
- GList *l;
-
- g_fprintf (output, "\n");
- for (l = element->children; l; l = l->next)
- {
- Element *child = l->data;
- dump_element (child, output, indent + 2);
- }
- g_fprintf (output, "%*s", indent, "");
- }
- else
- {
- if (is_cdata_property (element))
- {
- g_fprintf (output, "<![CDATA[");
- g_fprintf (output, "%s", element->data);
- g_fprintf (output, "]]>");
- }
- else
- {
- char *escaped = g_markup_escape_text (element->data, -1);
- g_fprintf (output, "%s", escaped);
- g_free (escaped);
- }
- }
- g_fprintf (output, "</%s>\n", element->element_name);
- }
- else
- g_fprintf (output, "/>\n");
-}
-
-static void
-write_xml_declaration (FILE *output)
-{
- g_fprintf (output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-}
-
-static void
-dump_tree (MyParserData *data)
-{
- write_xml_declaration (data->output);
- dump_element (data->root, data->output, 0);
-}
-
-static gboolean
-simplify_file (const char *filename,
- gboolean replace,
- gboolean convert3to4)
-{
- GMarkupParseContext *context;
- char *buffer;
- MyParserData data;
- GError *error = NULL;
-
- data.input_filename = filename;
- data.output_filename = NULL;
- data.convert3to4 = convert3to4;
- data.has_gtk_requires = FALSE;
-
- if (replace)
- {
- int fd;
- fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
- data.output = fdopen (fd, "w");
- }
- else
- {
- data.output = stdout;
- }
-
- if (!g_file_get_contents (filename, &buffer, NULL, &error))
- {
- g_printerr (_("Can’t load “%s”: %s\n"), filename, error->message);
- return FALSE;
- }
-
- data.root = NULL;
- data.current = NULL;
- data.value = g_string_new ("");
-
- context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
- if (!g_markup_parse_context_parse (context, buffer, -1, &error))
- {
- g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
- return FALSE;
- }
-
- data.builder = gtk_builder_new ();
-
- if (data.convert3to4)
- {
- enhance_tree (&data);
- rewrite_tree (&data);
- }
- simplify_tree (&data);
-
- dump_tree (&data);
-
- fclose (data.output);
-
- if (data.output_filename)
- {
- char *content;
- gsize length;
-
- if (!g_file_get_contents (data.output_filename, &content, &length, &error))
- {
- g_printerr (_("Failed to read “%s”: %s\n"), data.output_filename, error->message);
- return FALSE;
- }
-
- if (!g_file_set_contents (data.input_filename, content, length, &error))
- {
- g_printerr (_("Failed to write %s: “%s”\n"), data.input_filename, error->message);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-void
-do_simplify (int *argc,
- const char ***argv)
-{
- gboolean replace = FALSE;
- gboolean convert3to4 = FALSE;
- char **filenames = NULL;
- GOptionContext *ctx;
- const GOptionEntry entries[] = {
- { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
- { "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
- { NULL, }
- };
- GError *error = NULL;
- int i;
-
- ctx = g_option_context_new (NULL);
- g_option_context_set_help_enabled (ctx, FALSE);
- g_option_context_add_main_entries (ctx, entries, NULL);
-
- if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
- {
- g_printerr ("%s\n", error->message);
- g_error_free (error);
- exit (1);
- }
-
- g_option_context_free (ctx);
-
- if (filenames == NULL)
- {
- g_printerr (_("No .ui file specified\n"));
- exit (1);
- }
-
- if (g_strv_length (filenames) > 1 && !replace)
- {
- g_printerr (_("Can only simplify a single .ui file without --replace\n"));
- exit (1);
- }
-
- for (i = 0; filenames[i]; i++)
- {
- if (!simplify_file (filenames[i], replace, convert3to4))
- exit (1);
- }
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static GType
-make_fake_type (const char *type_name,
- const char *parent_name)
-{
- GType parent_type;
- GTypeQuery query;
-
- parent_type = g_type_from_name (parent_name);
- if (parent_type == G_TYPE_INVALID)
- {
- g_printerr ("Failed to lookup template parent type %s\n", parent_name);
- exit (1);
- }
-
- g_type_query (parent_type, &query);
- return g_type_register_static_simple (parent_type,
- type_name,
- query.class_size,
- NULL,
- query.instance_size,
- NULL,
- 0);
-}
-
-static void
-do_validate_template (const char *filename,
- const char *type_name,
- const char *parent_name)
-{
- GType template_type;
- GObject *object;
- GtkBuilder *builder;
- GError *error = NULL;
- int ret;
-
- /* Only make a fake type if it doesn't exist yet.
- * This lets us e.g. validate the GtkFileChooserWidget template.
- */
- template_type = g_type_from_name (type_name);
- if (template_type == G_TYPE_INVALID)
- template_type = make_fake_type (type_name, parent_name);
-
- object = g_object_new (template_type, NULL);
- if (!object)
- {
- g_printerr ("Failed to create an instance of the template type %s\n", type_name);
- exit (1);
- }
-
- builder = gtk_builder_new ();
- ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
- if (ret)
- ret = gtk_builder_add_from_file (builder, filename, &error);
- g_object_unref (builder);
-
- if (ret == 0)
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-}
-
-static gboolean
-parse_template_error (const char *message,
- char **class_name,
- char **parent_name)
-{
- char *p;
-
- p = strstr (message, "(class '");
- if (p)
- {
- *class_name = g_strdup (p + strlen ("(class '"));
- p = strstr (*class_name, "'");
- if (p)
- *p = '\0';
- }
- p = strstr (message, ", parent '");
- if (p)
- {
- *parent_name = g_strdup (p + strlen (", parent '"));
- p = strstr (*parent_name, "'");
- if (p)
- *p = '\0';
- }
-
- return TRUE;
-}
-
-static gboolean
-validate_file (const char *filename)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- int ret;
- char *class_name = NULL;
- char *parent_name = NULL;
-
- builder = gtk_builder_new ();
- ret = gtk_builder_add_from_file (builder, filename, &error);
- g_object_unref (builder);
-
- if (ret == 0)
- {
- if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
- parse_template_error (error->message, &class_name, &parent_name))
- {
- do_validate_template (filename, class_name, parent_name);
- }
- else
- {
- g_printerr ("%s\n", error->message);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-void
-do_validate (int *argc, const char ***argv)
-{
- int i;
-
- for (i = 1; i < *argc; i++)
- {
- if (!validate_file ((*argv)[i]))
- exit (1);
- }
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static void G_GNUC_NORETURN
-usage (void)
-{
- g_print (_("Usage:\n"
- " gtk-builder-tool [COMMAND] [OPTION…] FILE\n"
- "\n"
- "Commands:\n"
- " validate Validate the file\n"
- " simplify Simplify the file\n"
- " enumerate List all named objects\n"
- " preview Preview the file\n"
- "\n"
- "Simplify Options:\n"
- " --replace Replace the file\n"
- " --3to4 Convert from GTK 3 to GTK 4\n"
- "\n"
- "Preview Options:\n"
- " --id=ID Preview only the named object\n"
- " --css=FILE Use style from CSS file\n"
- "\n"
- "Perform various tasks on GtkBuilder .ui files.\n"));
- exit (1);
-}
-
-/* A simplified version of g_log_writer_default_would_drop(), to avoid
- * bumping up the required version of GLib to 2.68
- */
-static gboolean
-would_drop (GLogLevelFlags level,
- const char *domain)
-{
- return (level & (G_LOG_LEVEL_ERROR |
- G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING)) == 0;
-}
-
-static GLogWriterOutput
-log_writer_func (GLogLevelFlags level,
- const GLogField *fields,
- gsize n_fields,
- gpointer user_data)
-{
- gsize i;
- const char *domain = NULL;
- const char *message = NULL;
-
- for (i = 0; i < n_fields; i++)
- {
- if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
- domain = fields[i].value;
- else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
- message = fields[i].value;
- }
-
- if (message != NULL && !would_drop (level, domain))
- {
- const char *prefix;
- switch (level & G_LOG_LEVEL_MASK)
- {
- case G_LOG_LEVEL_ERROR:
- prefix = "ERROR";
- break;
- case G_LOG_LEVEL_CRITICAL:
- prefix = "CRITICAL";
- break;
- case G_LOG_LEVEL_WARNING:
- prefix = "WARNING";
- break;
- default:
- prefix = "INFO";
- break;
- }
- g_printerr ("%s-%s: %s\n", domain, prefix, message);
- }
-
- return G_LOG_WRITER_HANDLED;
-}
-
-int
-main (int argc, const char *argv[])
-{
- g_set_prgname ("gtk-builder-tool");
-
- g_log_set_writer_func (log_writer_func, NULL, NULL);
-
- gtk_init ();
-
- gtk_test_register_all_types ();
-
- if (argc < 3)
- usage ();
-
- if (strcmp (argv[2], "--help") == 0)
- usage ();
-
- argv++;
- argc--;
-
- if (strcmp (argv[0], "validate") == 0)
- do_validate (&argc, &argv);
- else if (strcmp (argv[0], "simplify") == 0)
- do_simplify (&argc, &argv);
- else if (strcmp (argv[0], "enumerate") == 0)
- do_enumerate (&argc, &argv);
- else if (strcmp (argv[0], "preview") == 0)
- do_preview (&argc, &argv);
- else
- usage ();
-
- return 0;
-}
+++ /dev/null
-
-#ifndef __GTK_BUILDER_TOOL_H__
-#define __GTK_BUILDER_TOOL_H__
-
-void do_simplify (int *argc, const char ***argv);
-void do_validate (int *argc, const char ***argv);
-void do_enumerate (int *argc, const char ***argv);
-void do_preview (int *argc, const char ***argv);
-
-#endif
+++ /dev/null
-/* GTK - The GIMP Toolkit
- *
- * Copyright (C) 2012 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Tomas Bzatek <tbzatek@redhat.com>
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <locale.h>
-#include <errno.h>
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
-#include <gio/gdesktopappinfo.h>
-#endif
-#include <gtk.h>
-
-static gboolean show_version;
-static char **args = NULL;
-
-static GOptionEntry entries[] = {
- { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
- { NULL}
-};
-
-int
-main (int argc, char *argv[])
-{
- GError *error = NULL;
- GOptionContext *context = NULL;
- char *summary;
- char *app_name;
-#ifdef G_OS_UNIX
- char *desktop_file_name;
- char *bus_name = NULL;
-#endif
- GAppInfo *info = NULL;
- GAppLaunchContext *launch_context;
- GList *l;
- GFile *f;
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- context =
- /* Translators: this message will appear immediately after the */
- /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE> */
- g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
-
- /* Translators: this message will appear after the usage string */
- /* and before the list of options. */
- summary = _("Launch an application (specified by its desktop file name),\n"
- "optionally passing one or more URIs as arguments.");
- g_option_context_set_summary (context, summary);
- g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
- g_option_context_parse (context, &argc, &argv, &error);
-
- g_option_context_free (context);
-
- if (error != NULL)
- {
- g_printerr (_("Error parsing commandline options: %s\n"), error->message);
- g_printerr ("\n");
- g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
- g_printerr ("\n");
- g_error_free (error);
- return 1;
- }
-
- if (show_version)
- {
- g_print ("%d.%d.%d\n",
- gtk_get_major_version (),
- gtk_get_minor_version (),
- gtk_get_micro_version ());
- return 0;
- }
-
- if (!args)
- {
- /* Translators: the %s is the program name. This error message */
- /* means the user is calling gtk-launch without any argument. */
- g_printerr (_("%s: missing application name"), g_get_prgname ());
- g_printerr ("\n");
- g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
- g_printerr ("\n");
- return 1;
- }
-
-
- gtk_init ();
-
- app_name = *args;
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
- bus_name = g_strdup (app_name);
- if (g_str_has_suffix (app_name, ".desktop"))
- {
- desktop_file_name = g_strdup (app_name);
- bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
- }
- else
- {
- desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
- }
-
- if (!g_dbus_is_name (bus_name))
- g_clear_pointer (&bus_name, g_free);
- info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
- g_free (desktop_file_name);
-#else
-#warning Please add support for creating AppInfo from id for your OS
- g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
-#endif
- args++;
-
- if (!info)
- {
- /* Translators: the first %s is the program name, the second one */
- /* is the application name. */
- g_printerr (_("%s: no such application %s"),
- g_get_prgname (), app_name);
- g_printerr ("\n");
- return 2;
- }
-
- l = NULL;
- for (; *args; args++)
- {
- f = g_file_new_for_commandline_arg (*args);
- l = g_list_append (l, f);
- }
-
- launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
- if (!g_app_info_launch (info, l, launch_context, &error))
- {
- /* Translators: the first %s is the program name, the second one */
- /* is the error message. */
- g_printerr (_("%s: error launching application: %s\n"),
- g_get_prgname (), error->message);
- return 3;
- }
- g_object_unref (info);
- g_object_unref (launch_context);
-
-#ifdef G_OS_UNIX
- if (bus_name != NULL)
- {
- GDBusConnection *connection;
- char *object_path, *p;
-
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
-
- object_path = g_strdup_printf ("/%s", bus_name);
- for (p = object_path; *p != '\0'; p++)
- if (*p == '.')
- *p = '/';
-
- if (connection)
- g_dbus_connection_call_sync (connection,
- bus_name,
- object_path,
- "org.freedesktop.DBus.Peer",
- "Ping",
- NULL, NULL,
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
- g_clear_pointer (&object_path, g_free);
- g_clear_object (&connection);
- g_clear_pointer (&bus_name, g_free);
- }
-#endif
- g_list_free_full (l, g_object_unref);
-
- return 0;
-}
+++ /dev/null
-/* Copyright 2015 Timm Bäder
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <string.h>
-
-
-int
-main (int argc, char **argv)
-{
- GtkSettings *settings;
- GParamSpec **props;
- guint n_properties;
- guint i;
- int max_prop_name_length = 0;
- char *pattern = NULL;
-
- gtk_init ();
-
- if (argc > 1)
- pattern = argv[1];
-
- settings = gtk_settings_get_default ();
- props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
-
- for (i = 0; i < n_properties; i ++)
- {
- int len = strlen (props[i]->name);
-
- if (len > max_prop_name_length)
- max_prop_name_length = len;
- }
-
-
- for (i = 0; i < n_properties; i ++)
- {
- GValue value = {0};
- GParamSpec *prop = props[i];
- char *value_str;
- int spacing = max_prop_name_length - strlen (prop->name) + 1;
- gboolean deprecated;
-
- if (pattern && !g_strrstr (prop->name, pattern))
- continue;
-
- g_value_init (&value, prop->value_type);
- g_object_get_property (G_OBJECT (settings), prop->name, &value);
- deprecated = prop->flags & G_PARAM_DEPRECATED;
-
- if (G_VALUE_HOLDS_ENUM (&value))
- {
- GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
- GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
-
- value_str = g_strdup (enum_value->value_name);
- }
- else
- {
- value_str = g_strdup_value_contents (&value);
- }
-
- if (deprecated)
- {
- printf ("!");
- spacing --;
- }
-
- for (; spacing >= 0; spacing --)
- printf (" ");
-
- printf ("%s: %s\n", prop->name, value_str);
-
- g_free (value_str);
- g_value_unset (&value);
- }
-
- g_free (props);
-
- return 0;
-}
+++ /dev/null
-<?xml version="1.0"?>
-<its:rules xmlns:its="http://www.w3.org/2005/11/its"
- xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
- version="2.0">
- <its:translateRule selector="/interface" translate="no"/>
- <its:translateRule selector="/interface//*[@translatable = 'yes']"
- translate="yes"/>
-
- <!-- The 'comment' attribute should be extracted as a translator comment. -->
- <its:locNoteRule selector="/interface//*[@comments]"
- locNotePointer="@comments"
- locNoteType="alert"/>
- <gt:escapeRule selector="/interface//@comments" escape="no"/>
-
- <!-- The 'context' attribute should be extracted as msgctxt. -->
- <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
-
- <its:preserveSpaceRule selector="/interface" space="preserve"/>
-
- <!-- Extracted strings are consumed by the library and are never
- merged back; we don't want to escape special characters. -->
- <gt:escapeRule selector="/interface" escape="no"/>
-</its:rules>
+++ /dev/null
-<?xml version="1.0"?>
-<locatingRules>
- <locatingRule name="GtkBuilder" pattern="*.ui">
- <documentRule localName="interface" target="gtkbuilder.its"/>
- </locatingRule>
-</locatingRules>
+++ /dev/null
-<?xml version="1.0"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
- <start>
- <element name="interface">
- <optional>
- <attribute name="domain">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="requires"/>
- <ref name="object"/>
- <ref name="template"/>
- <ref name="menu"/>
- </choice>
- </zeroOrMore>
- </element>
- </start>
- <define name="requires">
- <element name="requires">
- <attribute name="lib">
- <text/>
- </attribute>
- <attribute name="version">
- <text/>
- </attribute>
- </element>
- </define>
- <define name="object">
- <element name="object">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <attribute name="class">
- <text/>
- </attribute>
- <optional>
- <attribute name="type-func">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="constructor">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="property"/>
- <ref name="signal"/>
- <ref name="child"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="template">
- <element name="template">
- <attribute name="class">
- <text/>
- </attribute>
- <attribute name="parent">
- <text/>
- </attribute>
- <zeroOrMore>
- <choice>
- <ref name="property"/>
- <ref name="signal"/>
- <ref name="child"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="property">
- <element name="property">
- <attribute name="name">
- <text/>
- </attribute>
- <optional>
- <attribute name="translatable">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <optional>
- <attribute name="comments">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="context">
- <text/>
- </attribute>
- </optional>
- <optional>
- <group>
- <attribute name="bind-source">
- <text/>
- </attribute>
- <optional>
- <attribute name="bind-property">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="bind-flags">
- <text/>
- </attribute>
- </optional>
- </group>
- </optional>
- <optional>
- <text/>
- </optional>
- </element>
- </define>
- <define name="signal">
- <element name="signal">
- <attribute name="name">
- <text/>
- </attribute>
- <attribute name="handler">
- <text/>
- </attribute>
- <optional>
- <attribute name="after">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="swapped">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="object">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="last_modification_time">
- <text/>
- </attribute>
- </optional>
- <empty/>
- </element>
- </define>
- <define name="child">
- <element name="child">
- <optional>
- <attribute name="type">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="internal-child">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="object"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="menu">
- <element name="menu">
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- <optional>
- <attribute name="domain">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="item">
- <element name="item">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="link"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="attribute_">
- <element name="attribute">
- <attribute name="name">
- <text/>
- </attribute>
- <optional>
- <attribute name="type">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="translatable">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <optional>
- <attribute name="context">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="comments">
- <text/>
- </attribute>
- </optional>
- <optional>
- <text/>
- </optional>
- </element>
- </define>
- <define name="link">
- <element name="link">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <attribute name="name">
- <text/>
- </attribute>
- <zeroOrMore>
- <ref name="item"/>
- </zeroOrMore>
- </element>
- </define>
- <define name="submenu">
- <element name="submenu">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="section">
- <element name="section">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="ANY">
- <element>
- <anyName>
- <except>
- <name>interface</name>
- <name>requires</name>
- <name>object</name>
- <name>property</name>
- <name>signal</name>
- <name>child</name>
- <name>menu</name>
- <name>item</name>
- <name>attribute</name>
- <name>link</name>
- <name>submenu</name>
- <name>section</name>
- </except>
- </anyName>
- <zeroOrMore>
- <attribute>
- <anyName/>
- <text/>
- </attribute>
- </zeroOrMore>
- <interleave>
- <zeroOrMore>
- <ref name="ALL"/>
- </zeroOrMore>
- <optional>
- <text/>
- </optional>
- </interleave>
- </element>
- </define>
- <define name="ALL">
- <element>
- <anyName/>
- <zeroOrMore>
- <attribute>
- <anyName/>
- <text/>
- </attribute>
- </zeroOrMore>
- <interleave>
- <zeroOrMore>
- <ref name="ALL"/>
- </zeroOrMore>
- <optional>
- <text/>
- </optional>
- </interleave>
- </element>
- </define>
-</grammar>
+++ /dev/null
-/* gtkiconcachevalidator.c
- * Copyright (C) 2007 Red Hat, Inc
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
-#include "gtkiconcachevalidatorprivate.h"
-
-#include <glib.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-
-
-#define VERBOSE(x)
-
-#define check(name,condition) \
- if (!(condition)) \
- { \
- VERBOSE(g_message ("bad %s", (name))); \
- return FALSE; \
- }
-
-static inline gboolean
-get_uint16 (CacheInfo *info,
- guint32 offset,
- guint16 *value)
-{
- if (offset < info->cache_size)
- {
- *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
- return TRUE;
- }
- else
- {
- *value = 0;
- return FALSE;
- }
-}
-
-static inline gboolean
-get_uint32 (CacheInfo *info,
- guint32 offset,
- guint32 *value)
-{
- if (offset < info->cache_size)
- {
- *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
- return TRUE;
- }
- else
- {
- *value = 0;
- return FALSE;
- }
-}
-
-static gboolean
-check_version (CacheInfo *info)
-{
- guint16 major, minor;
-
- check ("major version", get_uint16 (info, 0, &major) && major == 1);
- check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
-
- return TRUE;
-}
-
-static gboolean
-check_string (CacheInfo *info,
- guint32 offset)
-{
- check ("string offset", offset < info->cache_size);
-
- if (info->flags & CHECK_STRINGS)
- {
- int i;
- char c;
-
- /* assume no string is longer than 1k */
- for (i = 0; i < 1024; i++)
- {
- check ("string offset", offset + i < info->cache_size)
- c = *(info->cache + offset + i);
- if (c == '\0')
- break;
- check ("string content", g_ascii_isgraph (c));
- }
- check ("string length", i < 1024);
- }
-
- return TRUE;
-}
-
-static gboolean
-check_string_utf8 (CacheInfo *info,
- guint32 offset)
-{
- check ("string offset", offset < info->cache_size);
-
- if (info->flags & CHECK_STRINGS)
- {
- int i;
- char c;
-
- /* assume no string is longer than 1k */
- for (i = 0; i < 1024; i++)
- {
- check ("string offset", offset + i < info->cache_size)
- c = *(info->cache + offset + i);
- if (c == '\0')
- break;
- }
- check ("string length", i < 1024);
- check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
- }
-
- return TRUE;
-}
-
-static gboolean
-check_directory_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 directory_offset;
- int i;
-
- check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
-
- for (i = 0; i < info->n_directories; i++)
- {
- check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
- if (!check_string (info, directory_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_pixel_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 type;
- guint32 length;
-
- check ("offset, pixel data type", get_uint32 (info, offset, &type));
- check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
-
- check ("pixel data type", type == 0);
- check ("pixel data length", offset + 8 + length < info->cache_size);
-
- if (info->flags & CHECK_PIXBUFS)
- {
- GdkPixdata data;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- check ("pixel data", gdk_pixdata_deserialize (&data, length,
- (const guint8*)info->cache + offset + 8,
- NULL));
-G_GNUC_END_IGNORE_DEPRECATIONS;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_embedded_rect (CacheInfo *info,
- guint32 offset)
-{
- check ("embedded rect", offset + 4 < info->cache_size);
-
- return TRUE;
-}
-
-static gboolean
-check_attach_point_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_attach_points;
-
- check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
- check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
-
- return TRUE;
-}
-
-static gboolean
-check_display_name_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_display_names, ofs;
- int i;
-
- check ("offset, display name list",
- get_uint32 (info, offset, &n_display_names));
- for (i = 0; i < n_display_names; i++)
- {
- get_uint32(info, offset + 4 + 8 * i, &ofs);
- if (!check_string (info, ofs))
- return FALSE;
- get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
- if (!check_string_utf8 (info, ofs))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_meta_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 embedded_rect_offset;
- guint32 attach_point_list_offset;
- guint32 display_name_list_offset;
-
- check ("offset, embedded rect",
- get_uint32 (info, offset, &embedded_rect_offset));
- check ("offset, attach point list",
- get_uint32 (info, offset + 4, &attach_point_list_offset));
- check ("offset, display name list",
- get_uint32 (info, offset + 8, &display_name_list_offset));
-
- if (embedded_rect_offset != 0)
- {
- if (!check_embedded_rect (info, embedded_rect_offset))
- return FALSE;
- }
-
- if (attach_point_list_offset != 0)
- {
- if (!check_attach_point_list (info, attach_point_list_offset))
- return FALSE;
- }
-
- if (display_name_list_offset != 0)
- {
- if (!check_display_name_list (info, display_name_list_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 pixel_data_offset;
- guint32 meta_data_offset;
-
- check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
- check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
-
- if (pixel_data_offset != 0)
- {
- if (!check_pixel_data (info, pixel_data_offset))
- return FALSE;
- }
- if (meta_data_offset != 0)
- {
- if (!check_meta_data (info, meta_data_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image (CacheInfo *info,
- guint32 offset)
-{
- guint16 index;
- guint16 flags;
- guint32 image_data_offset;
-
- check ("offset, image index", get_uint16 (info, offset, &index));
- check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
- check ("offset, image data offset",
- get_uint32 (info, offset + 4, &image_data_offset));
-
- check ("image index", index < info->n_directories);
- check ("image flags", flags < 16);
-
- if (image_data_offset != 0)
- {
- if (!check_image_data (info, image_data_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_images;
- int i;
-
- check ("offset, image list", get_uint32 (info, offset, &n_images));
-
- for (i = 0; i < n_images; i++)
- {
- if (!check_image (info, offset + 4 + 8 * i))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_icon (CacheInfo *info,
- guint32 offset)
-{
- guint32 chain_offset;
- guint32 name_offset;
- guint32 image_list_offset;
-
- check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
- check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
- check ("offset, icon image list", get_uint32 (info, offset + 8,
- &image_list_offset));
-
- if (!check_string (info, name_offset))
- return FALSE;
- if (!check_image_list (info, image_list_offset))
- return FALSE;
- if (chain_offset != 0xffffffff)
- {
- if (!check_icon (info, chain_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_hash (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_buckets, icon_offset;
- int i;
-
- check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
-
- for (i = 0; i < n_buckets; i++)
- {
- check ("offset, hash chain",
- get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
- if (icon_offset != 0xffffffff)
- {
- if (!check_icon (info, icon_offset))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- * gtk_icon_cache_validate:
- * @info: a CacheInfo structure
- *
- * Validates the icon cache passed in the @cache and
- * @cache_size fields of the @info structure. The
- * validator checks that offsets specified in the
- * cache do not point outside the mapped area, that
- * strings look reasonable, and that pixbufs can
- * be deserialized. The amount of validation can
- * be controlled with the @flags field.
- *
- * Returns: %TRUE if the cache is valid
- */
-gboolean
-gtk_icon_cache_validate (CacheInfo *info)
-{
- guint32 hash_offset;
- guint32 directory_list_offset;
-
- if (!check_version (info))
- return FALSE;
- check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
- check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
- if (!check_directory_list (info, directory_list_offset))
- return FALSE;
-
- if (!check_hash (info, hash_offset))
- return FALSE;
-
- return TRUE;
-}
-
+++ /dev/null
-# Installed tools
-gtk_tools = [
- ['gtk4-query-settings', ['gtk-query-settings.c']],
- ['gtk4-builder-tool', ['gtk-builder-tool.c',
- 'gtk-builder-tool-simplify.c',
- 'gtk-builder-tool-validate.c',
- 'gtk-builder-tool-enumerate.c',
- 'gtk-builder-tool-preview.c']],
- ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']],
- ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']],
-]
-
-if os_unix
- gtk_tools += [['gtk4-launch', ['gtk-launch.c']]]
-endif
-
-foreach tool: gtk_tools
- tool_name = tool.get(0)
- tool_srcs = tool.get(1)
-
- exe = executable(tool_name,
- sources: tool_srcs,
- include_directories: [confinc],
- c_args: common_cflags,
- dependencies: libgtk_dep,
- install: true,
- )
-
- set_variable(tool_name.underscorify(), exe) # used in testsuites
-endforeach
-
-# Data to install
-install_data('gtk4builder.rng', install_dir: gtk_datadir / 'gtk-4.0')
-
-install_data([
- 'gtk4builder.loc',
- 'gtk4builder.its',
- ],
- install_dir: gtk_datadir / 'gettext/its',
-)
+++ /dev/null
-/* updateiconcache.c
- * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <locale.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#ifdef _MSC_VER
-#include <io.h>
-#include <sys/utime.h>
-#else
-#include <utime.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-#include <glib/gi18n.h>
-#include "gtkiconcachevalidatorprivate.h"
-
-static gboolean force_update = FALSE;
-static gboolean ignore_theme_index = FALSE;
-static gboolean quiet = FALSE;
-static gboolean index_only = TRUE;
-static gboolean validate = FALSE;
-static char *var_name = (char *) "-";
-
-#define CACHE_NAME "icon-theme.cache"
-
-#define HAS_SUFFIX_XPM (1 << 0)
-#define HAS_SUFFIX_SVG (1 << 1)
-#define HAS_SUFFIX_PNG (1 << 2)
-#define HAS_ICON_FILE (1 << 3)
-
-#define MAJOR_VERSION 1
-#define MINOR_VERSION 0
-#define HASH_OFFSET 12
-
-#define ALIGN_VALUE(this, boundary) \
- (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
-
-#ifdef HAVE_FTW_H
-
-#include <ftw.h>
-
-static GStatBuf cache_dir_stat;
-static gboolean cache_up_to_date;
-
-static int check_dir_mtime (const char *dir,
- const struct stat *sb,
- int tf)
-{
- if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
- {
- cache_up_to_date = FALSE;
- /* stop tree walk */
- return 1;
- }
-
- return 0;
-}
-
-static gboolean
-is_cache_up_to_date (const char *path)
-{
- char *cache_path;
- int retval;
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- retval = g_stat (cache_path, &cache_dir_stat);
- g_free (cache_path);
-
- if (retval < 0)
- {
- /* Cache file not found */
- return FALSE;
- }
-
- cache_up_to_date = TRUE;
-
- ftw (path, check_dir_mtime, 20);
-
- return cache_up_to_date;
-}
-
-#else /* !HAVE_FTW_H */
-
-gboolean
-is_cache_up_to_date (const char *path)
-{
- GStatBuf path_stat, cache_stat;
- char *cache_path;
- int retval;
-
- retval = g_stat (path, &path_stat);
-
- if (retval < 0)
- {
- /* We can't stat the path,
- * assume we have a updated cache */
- return TRUE;
- }
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- retval = g_stat (cache_path, &cache_stat);
- g_free (cache_path);
-
- if (retval < 0)
- {
- /* Cache file not found */
- return FALSE;
- }
-
- /* Check mtime */
- return cache_stat.st_mtime >= path_stat.st_mtime;
-}
-
-#endif /* !HAVE_FTW_H */
-
-static gboolean
-has_theme_index (const char *path)
-{
- gboolean result;
- char *index_path;
-
- index_path = g_build_filename (path, "index.theme", NULL);
-
- result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
-
- g_free (index_path);
-
- return result;
-}
-
-
-typedef struct
-{
- GdkPixdata pixdata;
- gboolean has_pixdata;
- guint32 offset;
- guint size;
-} ImageData;
-
-typedef struct
-{
- int has_embedded_rect;
- int x0, y0, x1, y1;
-
- int n_attach_points;
- int *attach_points;
-
- int n_display_names;
- char **display_names;
-
- guint32 offset;
- int size;
-} IconData;
-
-static GHashTable *image_data_hash = NULL;
-static GHashTable *icon_data_hash = NULL;
-
-typedef struct
-{
- int flags;
- int dir_index;
-
- ImageData *image_data;
- guint pixel_data_size;
-
- IconData *icon_data;
- guint icon_data_size;
-} Image;
-
-
-static gboolean
-foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
-{
- Image *image = (Image *)value;
- GHashTable *files = user_data;
- GList *list;
- gboolean free_key = FALSE;
-
- if (image->flags == HAS_ICON_FILE)
- {
- /* just a .icon file, throw away */
- g_free (key);
- g_free (image);
-
- return TRUE;
- }
-
- list = g_hash_table_lookup (files, key);
- if (list)
- free_key = TRUE;
-
- list = g_list_prepend (list, value);
- g_hash_table_insert (files, key, list);
-
- if (free_key)
- g_free (key);
-
- return TRUE;
-}
-
-static IconData *
-load_icon_data (const char *path)
-{
- GKeyFile *icon_file;
- char **split;
- gsize length;
- char *str;
- char *split_point;
- int i;
- int *ivalues;
- GError *error = NULL;
- char **keys;
- gsize n_keys;
- IconData *data;
-
- icon_file = g_key_file_new ();
- g_key_file_set_list_separator (icon_file, ',');
- g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
- if (error)
- {
- g_error_free (error);
- g_key_file_free (icon_file);
-
- return NULL;
- }
-
- data = g_new0 (IconData, 1);
-
- ivalues = g_key_file_get_integer_list (icon_file,
- "Icon Data", "EmbeddedTextRectangle",
- &length, NULL);
- if (ivalues)
- {
- if (length == 4)
- {
- data->has_embedded_rect = TRUE;
- data->x0 = ivalues[0];
- data->y0 = ivalues[1];
- data->x1 = ivalues[2];
- data->y1 = ivalues[3];
- }
-
- g_free (ivalues);
- }
-
- str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
- if (str)
- {
- split = g_strsplit (str, "|", -1);
-
- data->n_attach_points = g_strv_length (split);
- data->attach_points = g_new (int, 2 * data->n_attach_points);
-
- for (i = 0; i < data->n_attach_points; ++i)
- {
- split_point = strchr (split[i], ',');
- if (split_point)
- {
- *split_point = 0;
- split_point++;
- data->attach_points[2 * i] = atoi (split[i]);
- data->attach_points[2 * i + 1] = atoi (split_point);
- }
- }
-
- g_strfreev (split);
- g_free (str);
- }
-
- keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
- data->display_names = g_new0 (char *, 2 * n_keys + 1);
- data->n_display_names = 0;
-
- for (i = 0; i < n_keys; i++)
- {
- char *lang, *name;
-
- if (g_str_has_prefix (keys[i], "DisplayName"))
- {
- char *open, *close = NULL;
-
- open = strchr (keys[i], '[');
-
- if (open)
- close = strchr (open, ']');
-
- if (open && close)
- {
- lang = g_strndup (open + 1, close - open - 1);
- name = g_key_file_get_locale_string (icon_file,
- "Icon Data", "DisplayName",
- lang, NULL);
- }
- else
- {
- lang = g_strdup ("C");
- name = g_key_file_get_string (icon_file,
- "Icon Data", "DisplayName",
- NULL);
- }
-
- data->display_names[2 * data->n_display_names] = lang;
- data->display_names[2 * data->n_display_names + 1] = name;
- data->n_display_names++;
- }
- }
-
- g_strfreev (keys);
-
- g_key_file_free (icon_file);
-
- /* -1 means not computed yet, the real value depends
- * on string pool state, and will be computed
- * later
- */
- data->size = -1;
-
- return data;
-}
-
-/*
- * This function was copied from gtkfilesystemunix.c, it should
- * probably go to GLib
- */
-static void
-canonicalize_filename (char *filename)
-{
- char *p, *q;
- gboolean last_was_slash = FALSE;
-
- p = filename;
- q = filename;
-
- while (*p)
- {
- if (*p == G_DIR_SEPARATOR)
- {
- if (!last_was_slash)
- *q++ = G_DIR_SEPARATOR;
-
- last_was_slash = TRUE;
- }
- else
- {
- if (last_was_slash && *p == '.')
- {
- if (*(p + 1) == G_DIR_SEPARATOR ||
- *(p + 1) == '\0')
- {
- if (*(p + 1) == '\0')
- break;
-
- p += 1;
- }
- else if (*(p + 1) == '.' &&
- (*(p + 2) == G_DIR_SEPARATOR ||
- *(p + 2) == '\0'))
- {
- if (q > filename + 1)
- {
- q--;
- while (q > filename + 1 &&
- *(q - 1) != G_DIR_SEPARATOR)
- q--;
- }
-
- if (*(p + 2) == '\0')
- break;
-
- p += 2;
- }
- else
- {
- *q++ = *p;
- last_was_slash = FALSE;
- }
- }
- else
- {
- *q++ = *p;
- last_was_slash = FALSE;
- }
- }
-
- p++;
- }
-
- if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
- q--;
-
- *q = '\0';
-}
-
-static char *
-follow_links (const char *path)
-{
- char *target;
- char *d, *s;
- char *path2 = NULL;
-
- path2 = g_strdup (path);
- while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
- {
- target = g_file_read_link (path2, NULL);
-
- if (target)
- {
- if (g_path_is_absolute (target))
- path2 = target;
- else
- {
- d = g_path_get_dirname (path2);
- s = g_build_filename (d, target, NULL);
- g_free (d);
- g_free (target);
- g_free (path2);
- path2 = s;
- }
- }
- else
- break;
- }
-
- if (strcmp (path, path2) == 0)
- {
- g_free (path2);
- path2 = NULL;
- }
-
- return path2;
-}
-
-static void
-maybe_cache_image_data (Image *image,
- const char *path)
-{
- if (!index_only && !image->image_data &&
- (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
- {
- GdkPixbuf *pixbuf;
- ImageData *idata;
- char *path2;
-
- idata = g_hash_table_lookup (image_data_hash, path);
- path2 = follow_links (path);
-
- if (path2)
- {
- ImageData *idata2;
-
- canonicalize_filename (path2);
-
- idata2 = g_hash_table_lookup (image_data_hash, path2);
-
- if (idata && idata2 && idata != idata2)
- g_error ("different idatas found for symlinked '%s' and '%s'\n",
- path, path2);
-
- if (idata && !idata2)
- g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
-
- if (!idata && idata2)
- {
- g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
- idata = idata2;
- }
- }
-
- if (!idata)
- {
- idata = g_new0 (ImageData, 1);
- g_hash_table_insert (image_data_hash, g_strdup (path), idata);
- if (path2)
- g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
- }
-
- if (!idata->has_pixdata)
- {
- pixbuf = gdk_pixbuf_new_from_file (path, NULL);
-
- if (pixbuf)
- {
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
-G_GNUC_END_IGNORE_DEPRECATIONS;
- idata->size = idata->pixdata.length + 8;
- idata->has_pixdata = TRUE;
- }
- }
-
- image->image_data = idata;
-
- g_free (path2);
- }
-}
-
-static void
-maybe_cache_icon_data (Image *image,
- const char *path)
-{
- if (g_str_has_suffix (path, ".icon"))
- {
- IconData *idata = NULL;
- char *path2 = NULL;
-
- idata = g_hash_table_lookup (icon_data_hash, path);
- path2 = follow_links (path);
-
- if (path2)
- {
- IconData *idata2;
-
- canonicalize_filename (path2);
-
- idata2 = g_hash_table_lookup (icon_data_hash, path2);
-
- if (idata && idata2 && idata != idata2)
- g_error ("different idatas found for symlinked '%s' and '%s'\n",
- path, path2);
-
- if (idata && !idata2)
- g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
-
- if (!idata && idata2)
- {
- g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
- idata = idata2;
- }
- }
-
- if (!idata)
- {
- idata = load_icon_data (path);
- g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
- if (path2)
- g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
- }
-
- image->icon_data = idata;
-
- g_free (path2);
- }
-}
-
-/*
- * Finds all dir separators and replaces them with “/”.
- * This makes sure that only /-separated paths are written in cache files,
- * maintaining compatibility with theme index files that use slashes as
- * directory separators on all platforms.
- */
-static void
-replace_backslashes_with_slashes (char *path)
-{
- size_t i;
- if (path == NULL)
- return;
- for (i = 0; path[i]; i++)
- if (G_IS_DIR_SEPARATOR (path[i]))
- path[i] = '/';
-}
-
-static GList *
-scan_directory (const char *base_path,
- const char *subdir,
- GHashTable *files,
- GList *directories,
- int depth)
-{
- GHashTable *dir_hash;
- GDir *dir;
- GList *list = NULL, *iterator = NULL;
- const char *name;
- char *dir_path;
- gboolean dir_added = FALSE;
- guint dir_index = 0xffff;
-
- dir_path = g_build_path ("/", base_path, subdir, NULL);
-
- /* FIXME: Use the gerror */
- dir = g_dir_open (dir_path, 0, NULL);
-
- if (!dir)
- return directories;
-
- dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
- while ((name = g_dir_read_name (dir)))
- {
- list = g_list_prepend (list, g_strdup (name));
- }
- list = g_list_sort (list, (GCompareFunc) strcmp);
- for (iterator = list; iterator; iterator = iterator->next)
- {
- name = iterator->data;
-
- char *path;
- gboolean retval;
- int flags = 0;
- Image *image;
- char *basename, *dot;
-
- path = g_build_filename (dir_path, name, NULL);
-
- retval = g_file_test (path, G_FILE_TEST_IS_DIR);
- if (retval)
- {
- char *subsubdir;
-
- if (subdir)
- subsubdir = g_build_path ("/", subdir, name, NULL);
- else
- subsubdir = g_strdup (name);
- directories = scan_directory (base_path, subsubdir, files,
- directories, depth + 1);
- g_free (subsubdir);
-
- continue;
- }
-
- /* ignore images in the toplevel directory */
- if (subdir == NULL)
- continue;
-
- retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
- if (retval)
- {
- if (g_str_has_suffix (name, ".png"))
- flags |= HAS_SUFFIX_PNG;
- else if (g_str_has_suffix (name, ".svg"))
- flags |= HAS_SUFFIX_SVG;
- else if (g_str_has_suffix (name, ".xpm"))
- flags |= HAS_SUFFIX_XPM;
- else if (g_str_has_suffix (name, ".icon"))
- flags |= HAS_ICON_FILE;
-
- if (flags == 0)
- continue;
-
- basename = g_strdup (name);
- dot = strrchr (basename, '.');
- *dot = '\0';
-
- image = g_hash_table_lookup (dir_hash, basename);
- if (!image)
- {
- if (!dir_added)
- {
- dir_added = TRUE;
- if (subdir)
- {
- dir_index = g_list_length (directories);
- directories = g_list_append (directories, g_strdup (subdir));
- }
- else
- dir_index = 0xffff;
- }
-
- image = g_new0 (Image, 1);
- image->dir_index = dir_index;
- g_hash_table_insert (dir_hash, g_strdup (basename), image);
- }
-
- image->flags |= flags;
-
- maybe_cache_image_data (image, path);
- maybe_cache_icon_data (image, path);
-
- g_free (basename);
- }
-
- g_free (path);
- }
-
- g_list_free_full (list, g_free);
- g_dir_close (dir);
-
- /* Move dir into the big file hash */
- g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
-
- g_hash_table_destroy (dir_hash);
-
- return directories;
-}
-
-typedef struct _HashNode HashNode;
-
-struct _HashNode
-{
- HashNode *next;
- char *name;
- GList *image_list;
- int offset;
-};
-
-static guint
-icon_name_hash (gconstpointer key)
-{
- const signed char *p = key;
- guint32 h = *p;
-
- if (h)
- for (p += 1; *p != '\0'; p++)
- h = (h << 5) - h + *p;
-
- return h;
-}
-
-typedef struct {
- int size;
- HashNode **nodes;
-} HashContext;
-
-static gboolean
-convert_to_hash (gpointer key, gpointer value, gpointer user_data)
-{
- HashContext *context = user_data;
- guint hash;
- HashNode *node;
-
- hash = icon_name_hash (key) % context->size;
-
- node = g_new0 (HashNode, 1);
- node->next = NULL;
- node->name = key;
- node->image_list = value;
-
- if (context->nodes[hash] != NULL)
- node->next = context->nodes[hash];
-
- context->nodes[hash] = node;
-
- return TRUE;
-}
-
-static GHashTable *string_pool = NULL;
-
-static int
-find_string (const char *n)
-{
- return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
-}
-
-static void
-add_string (const char *n, int offset)
-{
- g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
-}
-
-static gboolean
-write_string (FILE *cache, const char *n)
-{
- char *s;
- int i, l;
-
- l = ALIGN_VALUE (strlen (n) + 1, 4);
-
- s = g_malloc0 (l);
- strcpy (s, n);
-
- i = fwrite (s, l, 1, cache);
-
- g_free (s);
-
- return i == 1;
-
-}
-
-static gboolean
-write_card16 (FILE *cache, guint16 n)
-{
- int i;
-
- n = GUINT16_TO_BE (n);
-
- i = fwrite ((char *)&n, 2, 1, cache);
-
- return i == 1;
-}
-
-static gboolean
-write_card32 (FILE *cache, guint32 n)
-{
- int i;
-
- n = GUINT32_TO_BE (n);
-
- i = fwrite ((char *)&n, 4, 1, cache);
-
- return i == 1;
-}
-
-
-static gboolean
-write_image_data (FILE *cache, ImageData *image_data, int offset)
-{
- guint8 *s;
- guint len;
- int i;
- GdkPixdata *pixdata = &image_data->pixdata;
-
- /* Type 0 is GdkPixdata */
- if (!write_card32 (cache, 0))
- return FALSE;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- s = gdk_pixdata_serialize (pixdata, &len);
-G_GNUC_END_IGNORE_DEPRECATIONS;
-
- if (!write_card32 (cache, len))
- {
- g_free (s);
- return FALSE;
- }
-
- i = fwrite (s, len, 1, cache);
-
- g_free (s);
-
- return i == 1;
-}
-
-static gboolean
-write_icon_data (FILE *cache, IconData *icon_data, int offset)
-{
- int ofs = offset + 12;
- int j;
- int tmp, tmp2;
-
- if (icon_data->has_embedded_rect)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
-
- ofs += 8;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->n_attach_points > 0)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
-
- ofs += 4 + 4 * icon_data->n_attach_points;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->n_display_names > 0)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->has_embedded_rect)
- {
- if (!write_card16 (cache, icon_data->x0) ||
- !write_card16 (cache, icon_data->y0) ||
- !write_card16 (cache, icon_data->x1) ||
- !write_card16 (cache, icon_data->y1))
- return FALSE;
- }
-
- if (icon_data->n_attach_points > 0)
- {
- if (!write_card32 (cache, icon_data->n_attach_points))
- return FALSE;
-
- for (j = 0; j < 2 * icon_data->n_attach_points; j++)
- {
- if (!write_card16 (cache, icon_data->attach_points[j]))
- return FALSE;
- }
- }
-
- if (icon_data->n_display_names > 0)
- {
- if (!write_card32 (cache, icon_data->n_display_names))
- return FALSE;
-
- ofs += 4 + 8 * icon_data->n_display_names;
-
- tmp = ofs;
- for (j = 0; j < 2 * icon_data->n_display_names; j++)
- {
- tmp2 = find_string (icon_data->display_names[j]);
- if (tmp2 == 0 || tmp2 == -1)
- {
- tmp2 = tmp;
- tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
- /* We're playing a little game with negative
- * offsets here to handle duplicate strings in
- * the array.
- */
- add_string (icon_data->display_names[j], -tmp2);
- }
- else if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- }
-
- if (!write_card32 (cache, tmp2))
- return FALSE;
-
- }
-
- g_assert (ofs == ftell (cache));
- for (j = 0; j < 2 * icon_data->n_display_names; j++)
- {
- tmp2 = find_string (icon_data->display_names[j]);
- g_assert (tmp2 != 0 && tmp2 != -1);
- if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- g_assert (tmp2 == ftell (cache));
- add_string (icon_data->display_names[j], tmp2);
- if (!write_string (cache, icon_data->display_names[j]))
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-write_header (FILE *cache, guint32 dir_list_offset)
-{
- return (write_card16 (cache, MAJOR_VERSION) &&
- write_card16 (cache, MINOR_VERSION) &&
- write_card32 (cache, HASH_OFFSET) &&
- write_card32 (cache, dir_list_offset));
-}
-
-static int
-get_image_meta_data_size (Image *image)
-{
- int i;
-
- /* The complication with storing the size in both
- * IconData and Image is necessary since we attribute
- * the size of the IconData only to the first Image
- * using it (at which time it is written out in the
- * cache). Later Images just refer to the written out
- * IconData via the offset.
- */
- if (image->icon_data_size == 0)
- {
- if (image->icon_data && image->icon_data->size < 0)
- {
- IconData *data = image->icon_data;
-
- data->size = 0;
-
- if (data->has_embedded_rect ||
- data->n_attach_points > 0 ||
- data->n_display_names > 0)
- data->size += 12;
-
- if (data->has_embedded_rect)
- data->size += 8;
-
- if (data->n_attach_points > 0)
- data->size += 4 + data->n_attach_points * 4;
-
- if (data->n_display_names > 0)
- {
- data->size += 4 + 8 * data->n_display_names;
-
- for (i = 0; data->display_names[i]; i++)
- {
- int poolv;
- if ((poolv = find_string (data->display_names[i])) == 0)
- {
- data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
- /* Adding the string to the pool with -1
- * to indicate that it hasn't been written out
- * to the cache yet. We still need it in the
- * pool in case the same string occurs twice
- * during a get_single_node_size() calculation.
- */
- add_string (data->display_names[i], -1);
- }
- }
- }
-
- image->icon_data_size = data->size;
- data->size = 0;
- }
- }
-
- g_assert (image->icon_data_size % 4 == 0);
-
- return image->icon_data_size;
-}
-
-static int
-get_image_pixel_data_size (Image *image)
-{
- /* The complication with storing the size in both
- * ImageData and Image is necessary since we attribute
- * the size of the ImageData only to the first Image
- * using it (at which time it is written out in the
- * cache). Later Images just refer to the written out
- * ImageData via the offset.
- */
- if (image->pixel_data_size == 0)
- {
- if (image->image_data &&
- image->image_data->has_pixdata)
- {
- image->pixel_data_size = image->image_data->size;
- image->image_data->size = 0;
- }
- }
-
- g_assert (image->pixel_data_size % 4 == 0);
-
- return image->pixel_data_size;
-}
-
-static int
-get_image_data_size (Image *image)
-{
- int len;
-
- len = 0;
-
- len += get_image_pixel_data_size (image);
- len += get_image_meta_data_size (image);
-
- /* Even if len is zero, we need to reserve space to
- * write the ImageData, unless this is an .svg without
- * .icon, in which case both image_data and icon_data
- * are NULL.
- */
- if (len > 0 || image->image_data || image->icon_data)
- len += 8;
-
- return len;
-}
-
-static void
-get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
-{
- GList *list;
-
- /* Node pointers */
- *node_size = 12;
-
- /* Name */
- if (find_string (node->name) == 0)
- {
- *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
- add_string (node->name, -1);
- }
-
- /* Image list */
- *node_size += 4 + g_list_length (node->image_list) * 8;
-
- /* Image data */
- *image_data_size = 0;
- for (list = node->image_list; list; list = list->next)
- {
- Image *image = list->data;
-
- *image_data_size += get_image_data_size (image);
- }
-}
-
-static gboolean
-write_bucket (FILE *cache, HashNode *node, int *offset)
-{
- while (node != NULL)
- {
- int node_size, image_data_size;
- int next_offset, image_data_offset;
- int data_offset;
- int name_offset;
- int name_size;
- int image_list_offset;
- int i, len;
- GList *list;
-
- g_assert (*offset == ftell (cache));
-
- node->offset = *offset;
-
- get_single_node_size (node, &node_size, &image_data_size);
- g_assert (node_size % 4 == 0);
- g_assert (image_data_size % 4 == 0);
- image_data_offset = *offset + node_size;
- next_offset = *offset + node_size + image_data_size;
- /* Chain offset */
- if (node->next != NULL)
- {
- if (!write_card32 (cache, next_offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, 0xffffffff))
- return FALSE;
- }
-
- name_size = 0;
- name_offset = find_string (node->name);
- if (name_offset <= 0)
- {
- name_offset = *offset + 12;
- name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
- add_string (node->name, name_offset);
- }
- if (!write_card32 (cache, name_offset))
- return FALSE;
-
- image_list_offset = *offset + 12 + name_size;
- if (!write_card32 (cache, image_list_offset))
- return FALSE;
-
- /* Icon name */
- if (name_size > 0)
- {
- if (!write_string (cache, node->name))
- return FALSE;
- }
-
- /* Image list */
- len = g_list_length (node->image_list);
- if (!write_card32 (cache, len))
- return FALSE;
-
- list = node->image_list;
- data_offset = image_data_offset;
- for (i = 0; i < len; i++)
- {
- Image *image = list->data;
- int image_size = get_image_data_size (image);
-
- /* Directory index */
- if (!write_card16 (cache, image->dir_index))
- return FALSE;
-
- /* Flags */
- if (!write_card16 (cache, image->flags))
- return FALSE;
-
- /* Image data offset */
- if (image_size > 0)
- {
- if (!write_card32 (cache, data_offset))
- return FALSE;
- data_offset += image_size;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- list = list->next;
- }
-
- /* Now write the image data */
- list = node->image_list;
- for (i = 0; i < len; i++, list = list->next)
- {
- Image *image = list->data;
- int pixel_data_size = get_image_pixel_data_size (image);
- int meta_data_size = get_image_meta_data_size (image);
-
- if (get_image_data_size (image) == 0)
- continue;
-
- /* Pixel data */
- if (pixel_data_size > 0)
- {
- image->image_data->offset = image_data_offset + 8;
- if (!write_card32 (cache, image->image_data->offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
- return FALSE;
- }
-
- if (meta_data_size > 0)
- {
- image->icon_data->offset = image_data_offset + pixel_data_size + 8;
- if (!write_card32 (cache, image->icon_data->offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
- return FALSE;
- }
-
- if (pixel_data_size > 0)
- {
- if (!write_image_data (cache, image->image_data, image->image_data->offset))
- return FALSE;
- }
-
- if (meta_data_size > 0)
- {
- if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
- return FALSE;
- }
-
- image_data_offset += pixel_data_size + meta_data_size + 8;
- }
-
- *offset = next_offset;
- node = node->next;
- }
-
- return TRUE;
-}
-
-static gboolean
-write_hash_table (FILE *cache, HashContext *context, int *new_offset)
-{
- int offset = HASH_OFFSET;
- int node_offset;
- int i;
-
- if (!(write_card32 (cache, context->size)))
- return FALSE;
-
- offset += 4;
- node_offset = offset + context->size * 4;
- /* Just write zeros here, we will rewrite this later */
- for (i = 0; i < context->size; i++)
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- /* Now write the buckets */
- for (i = 0; i < context->size; i++)
- {
- if (!context->nodes[i])
- continue;
-
- g_assert (node_offset % 4 == 0);
- if (!write_bucket (cache, context->nodes[i], &node_offset))
- return FALSE;
- }
-
- *new_offset = node_offset;
-
- /* Now write out the bucket offsets */
-
- fseek (cache, offset, SEEK_SET);
-
- for (i = 0; i < context->size; i++)
- {
- if (context->nodes[i] != NULL)
- node_offset = context->nodes[i]->offset;
- else
- node_offset = 0xffffffff;
- if (!write_card32 (cache, node_offset))
- return FALSE;
- }
-
- fseek (cache, 0, SEEK_END);
-
- return TRUE;
-}
-
-static gboolean
-write_dir_index (FILE *cache, int offset, GList *directories)
-{
- int n_dirs;
- GList *d;
- char *dir;
- int tmp, tmp2;
-
- n_dirs = g_list_length (directories);
-
- if (!write_card32 (cache, n_dirs))
- return FALSE;
-
- offset += 4 + n_dirs * 4;
-
- tmp = offset;
- for (d = directories; d; d = d->next)
- {
- dir = d->data;
-
- tmp2 = find_string (dir);
-
- if (tmp2 == 0 || tmp2 == -1)
- {
- tmp2 = tmp;
- tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
- /* We're playing a little game with negative
- * offsets here to handle duplicate strings in
- * the array, even though that should not
- * really happen for the directory index.
- */
- add_string (dir, -tmp2);
- }
- else if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- }
-
- if (!write_card32 (cache, tmp2))
- return FALSE;
- }
-
- g_assert (offset == ftell (cache));
- for (d = directories; d; d = d->next)
- {
- dir = d->data;
-
- tmp2 = find_string (dir);
- g_assert (tmp2 != 0 && tmp2 != -1);
- if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- g_assert (tmp2 == ftell (cache));
- add_string (dir, tmp2);
- if (!write_string (cache, dir))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-write_file (FILE *cache, GHashTable *files, GList *directories)
-{
- HashContext context;
- int new_offset;
-
- /* Convert the hash table into something looking a bit more
- * like what we want to write to disk.
- */
- context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
- context.nodes = g_new0 (HashNode *, context.size);
-
- g_hash_table_foreach_remove (files, convert_to_hash, &context);
-
- /* Now write the file */
- /* We write 0 as the directory list offset and go
- * back and change it later */
- if (!write_header (cache, 0))
- {
- g_printerr (_("Failed to write header\n"));
- return FALSE;
- }
-
- if (!write_hash_table (cache, &context, &new_offset))
- {
- g_printerr (_("Failed to write hash table\n"));
- return FALSE;
- }
-
- if (!write_dir_index (cache, new_offset, directories))
- {
- g_printerr (_("Failed to write folder index\n"));
- return FALSE;
- }
-
- rewind (cache);
-
- if (!write_header (cache, new_offset))
- {
- g_printerr (_("Failed to rewrite header\n"));
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-validate_file (const char *file)
-{
- GMappedFile *map;
- CacheInfo info;
-
- map = g_mapped_file_new (file, FALSE, NULL);
- if (!map)
- return FALSE;
-
- info.cache = g_mapped_file_get_contents (map);
- info.cache_size = g_mapped_file_get_length (map);
- info.n_directories = 0;
- info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
-
- if (!gtk_icon_cache_validate (&info))
- {
- g_mapped_file_unref (map);
- return FALSE;
- }
-
- g_mapped_file_unref (map);
-
- return TRUE;
-}
-
-/**
- * safe_fclose:
- * @f: A FILE* stream, must have underlying fd
- *
- * Unix defaults for data preservation after system crash
- * are unspecified, and many systems will eat your data
- * in this situation unless you explicitly fsync().
- *
- * Returns: %TRUE on success, %FALSE on failure, and will set errno()
- */
-static gboolean
-safe_fclose (FILE *f)
-{
- int fd = fileno (f);
- g_assert (fd >= 0);
- if (fflush (f) == EOF)
- return FALSE;
-#ifndef G_OS_WIN32
- if (fsync (fd) < 0)
- return FALSE;
-#endif
- if (fclose (f) == EOF)
- return FALSE;
- return TRUE;
-}
-
-static void
-build_cache (const char *path)
-{
- char *cache_path, *tmp_cache_path;
-#ifdef G_OS_WIN32
- char *bak_cache_path = NULL;
-#endif
- GHashTable *files;
- FILE *cache;
- GStatBuf path_stat, cache_stat;
- struct utimbuf utime_buf;
- GList *directories = NULL;
- int fd;
- int retry_count = 0;
-#ifndef G_OS_WIN32
- mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-#else
- int mode = _S_IWRITE | _S_IREAD;
-#endif
-#ifndef _O_BINARY
-#define _O_BINARY 0
-#endif
-
- tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
-
-opentmp:
- if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
- {
- if (retry_count == 0)
- {
- retry_count++;
- g_remove (tmp_cache_path);
- goto opentmp;
- }
- g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
- exit (1);
- }
-
- cache = fdopen (fd, "wb");
-
- if (!cache)
- {
- g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
- exit (1);
- }
-
- files = g_hash_table_new (g_str_hash, g_str_equal);
- image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
- icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
- string_pool = g_hash_table_new (g_str_hash, g_str_equal);
-
- directories = scan_directory (path, NULL, files, NULL, 0);
-
- if (g_hash_table_size (files) == 0)
- {
- /* Empty table, just close and remove the file */
-
- fclose (cache);
- g_unlink (tmp_cache_path);
- g_unlink (cache_path);
- exit (0);
- }
-
- /* FIXME: Handle failure */
- if (!write_file (cache, files, directories))
- {
- g_unlink (tmp_cache_path);
- exit (1);
- }
-
- if (!safe_fclose (cache))
- {
- g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
- g_unlink (tmp_cache_path);
- exit (1);
- }
- cache = NULL;
-
- g_list_free_full (directories, g_free);
-
- if (!validate_file (tmp_cache_path))
- {
- g_printerr (_("The generated cache was invalid.\n"));
- /*g_unlink (tmp_cache_path);*/
- exit (1);
- }
-
-#ifdef G_OS_WIN32
- if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
- {
- bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
- g_unlink (bak_cache_path);
- if (g_rename (cache_path, bak_cache_path) == -1)
- {
- int errsv = errno;
-
- g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
- cache_path, bak_cache_path,
- g_strerror (errsv),
- cache_path);
- g_unlink (cache_path);
- bak_cache_path = NULL;
- }
- }
-#endif
-
- if (g_rename (tmp_cache_path, cache_path) == -1)
- {
- int errsv = errno;
-
- g_printerr (_("Could not rename %s to %s: %s\n"),
- tmp_cache_path, cache_path,
- g_strerror (errsv));
- g_unlink (tmp_cache_path);
-#ifdef G_OS_WIN32
- if (bak_cache_path != NULL)
- if (g_rename (bak_cache_path, cache_path) == -1)
- {
- errsv = errno;
-
- g_printerr (_("Could not rename %s back to %s: %s.\n"),
- bak_cache_path, cache_path,
- g_strerror (errsv));
- }
-#endif
- exit (1);
- }
-#ifdef G_OS_WIN32
- if (bak_cache_path != NULL)
- g_unlink (bak_cache_path);
-#endif
-
- /* Update time */
- /* FIXME: What do do if an error occurs here? */
- if (g_stat (path, &path_stat) < 0 ||
- g_stat (cache_path, &cache_stat))
- exit (1);
-
- utime_buf.actime = path_stat.st_atime;
- utime_buf.modtime = cache_stat.st_mtime;
-#if GLIB_CHECK_VERSION (2, 17, 1)
- g_utime (path, &utime_buf);
-#else
- utime (path, &utime_buf);
-#endif
-
- if (!quiet)
- g_printerr (_("Cache file created successfully.\n"));
-}
-
-static void
-write_csource (const char *path)
-{
- char *cache_path;
- char *data;
- gsize len;
- int i;
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- if (!g_file_get_contents (cache_path, &data, &len, NULL))
- exit (1);
-
- g_printf ("#ifdef __SUNPRO_C\n");
- g_printf ("#pragma align 4 (%s)\n", var_name);
- g_printf ("#endif\n");
-
- g_printf ("#ifdef __GNUC__\n");
- g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
- g_printf ("#else\n");
- g_printf ("static const guint8 %s[] = \n", var_name);
- g_printf ("#endif\n");
-
- g_printf ("{\n");
- for (i = 0; i < len - 1; i++)
- {
- if (i %12 == 0)
- g_printf (" ");
- g_printf ("0x%02x, ", (guint8)data[i]);
- if (i % 12 == 11)
- g_printf ("\n");
- }
-
- g_printf ("0x%02x\n};\n", (guint8)data[i]);
-}
-
-static GOptionEntry args[] = {
- { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
- { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
- { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
- { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
- { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
- { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
- { NULL }
-};
-
-static void
-printerr_handler (const char *string)
-{
- const char *charset;
-
- fputs (g_get_prgname (), stderr);
- fputs (": ", stderr);
- if (g_get_charset (&charset))
- fputs (string, stderr); /* charset is UTF-8 already */
- else
- {
- char *result;
-
- result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
-
- if (result)
- {
- fputs (result, stderr);
- g_free (result);
- }
-
- fflush (stderr);
- }
-}
-
-
-int
-main (int argc, char **argv)
-{
- char *path;
- GOptionContext *context;
-
- if (argc < 2)
- return 0;
-
- g_set_printerr_handler (printerr_handler);
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- context = g_option_context_new ("ICONPATH");
- g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
- g_option_context_parse (context, &argc, &argv, NULL);
-
- path = argv[1];
-#ifdef G_OS_WIN32
- path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
- if (validate)
- {
- char *file = g_build_filename (path, CACHE_NAME, NULL);
-
- if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
- {
- if (!quiet)
- g_printerr (_("File not found: %s\n"), file);
- exit (1);
- }
- if (!validate_file (file))
- {
- if (!quiet)
- g_printerr (_("Not a valid icon cache: %s\n"), file);
- exit (1);
- }
- else
- {
- exit (0);
- }
- }
-
- if (!ignore_theme_index && !has_theme_index (path))
- {
- if (path)
- {
- g_printerr (_("No theme index file.\n"));
- }
- else
- {
- g_printerr (_("No theme index file in “%s”.\n"
- "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
- }
-
- return 1;
- }
-
- if (!force_update && is_cache_up_to_date (path))
- return 0;
-
- replace_backslashes_with_slashes (path);
- build_cache (path);
-
- if (strcmp (var_name, "-") != 0)
- write_csource (path);
-
- return 0;
-}
subdir('gdk')
subdir('gsk')
subdir('gtk')
+subdir('tools')
subdir('modules')
if get_option('demos')
subdir('demos')
--- /dev/null
+/* encodesymbolic.c
+ * Copyright (C) 2014 Alexander Larsson <alexl@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "gdkpixbufutilsprivate.h"
+
+static char *output_dir = NULL;
+
+static gboolean debug;
+
+static GOptionEntry args[] = {
+ { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
+ { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Generate debug output") },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ char *path, *basename, *pngpath, *pngfile, *dot;
+ GOptionContext *context;
+ GdkPixbuf *symbolic;
+ GError *error;
+ int width, height;
+ char **sizev;
+ GFileOutputStream *out;
+ GFile *dest;
+ char *data;
+ gsize len;
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ g_set_prgname ("gtk-encode-symbolic-svg");
+
+ context = g_option_context_new ("[OPTION…] PATH WIDTHxHEIGHT");
+ g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (argc < 3)
+ {
+ g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
+ return 1;
+ }
+
+ width = 0;
+ height = 0;
+ sizev = g_strsplit (argv[2], "x", 0);
+ if (g_strv_length (sizev) == 2)
+ {
+ width = atoi(sizev[0]);
+ height = atoi(sizev[1]);
+ }
+ g_strfreev (sizev);
+
+ if (width == 0 || height == 0)
+ {
+ g_printerr (_("Invalid size %s\n"), argv[2]);
+ return 1;
+ }
+
+ path = argv[1];
+#ifdef G_OS_WIN32
+ path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+ error = NULL;
+ if (!g_file_get_contents (path, &data, &len, &error))
+ {
+ g_printerr (_("Can’t load file: %s\n"), error->message);
+ return 1;
+ }
+
+ basename = g_path_get_basename (path);
+
+ symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, debug ? basename : NULL, &error);
+ if (symbolic == NULL)
+ {
+ g_printerr (_("Can’t load file: %s\n"), error->message);
+ return 1;
+ }
+
+ g_free (data);
+
+ dot = strrchr (basename, '.');
+ if (dot != NULL)
+ *dot = 0;
+ pngfile = g_strconcat (basename, ".symbolic.png", NULL);
+ g_free (basename);
+
+ if (output_dir != NULL)
+ pngpath = g_build_filename (output_dir, pngfile, NULL);
+ else
+ pngpath = g_strdup (pngfile);
+
+ g_free (pngfile);
+
+ dest = g_file_new_for_path (pngpath);
+
+
+ out = g_file_replace (dest,
+ NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL, &error);
+ if (out == NULL)
+ {
+ g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+ return 1;
+ }
+
+ if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
+ {
+ g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+ return 1;
+ }
+
+ if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
+ {
+ g_printerr (_("Can’t close stream"));
+ return 1;
+ }
+
+ g_object_unref (out);
+ g_free (pngpath);
+
+ return 0;
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static const char *
+object_get_id (GObject *object)
+{
+ if (GTK_IS_BUILDABLE (object))
+ return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
+ else
+ return g_object_get_data (object, "gtk-builder-id");
+}
+
+void
+do_enumerate (int *argc, const char ***argv)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ int ret;
+ GSList *list, *l;
+ GObject *object;
+ const char *name;
+ const char *filename;
+
+ filename = (*argv)[1];
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+
+ if (ret == 0)
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+
+ list = gtk_builder_get_objects (builder);
+ for (l = list; l; l = l->next)
+ {
+ object = l->data;
+ name = object_get_id (object);
+ if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
+ continue;
+
+ g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
+ }
+ g_slist_free (list);
+
+ g_object_unref (builder);
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+
+static void
+set_window_title (GtkWindow *window,
+ const char *filename,
+ const char *id)
+{
+ char *name;
+ char *title;
+
+ name = g_path_get_basename (filename);
+
+ if (id)
+ title = g_strdup_printf ("%s in %s", id, name);
+ else
+ title = g_strdup (name);
+
+ gtk_window_set_title (window, title);
+
+ g_free (title);
+ g_free (name);
+}
+
+static void
+quit_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ gboolean *is_done = user_data;
+
+ *is_done = TRUE;
+
+ g_main_context_wakeup (NULL);
+}
+
+static void
+preview_file (const char *filename,
+ const char *id,
+ const char *cssfile)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ GObject *object;
+ GtkWidget *window;
+ gboolean done = FALSE;
+
+ if (cssfile)
+ {
+ GtkCssProvider *provider;
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_path (provider, cssfile);
+
+ gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+
+ builder = gtk_builder_new ();
+ if (!gtk_builder_add_from_file (builder, filename, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+
+ object = NULL;
+
+ if (id)
+ {
+ object = gtk_builder_get_object (builder, id);
+ }
+ else
+ {
+ GSList *objects, *l;
+
+ objects = gtk_builder_get_objects (builder);
+ for (l = objects; l; l = l->next)
+ {
+ GObject *obj = l->data;
+
+ if (GTK_IS_WINDOW (obj))
+ {
+ object = obj;
+ break;
+ }
+ else if (GTK_IS_WIDGET (obj))
+ {
+ if (object == NULL)
+ object = obj;
+ }
+ }
+ g_slist_free (objects);
+ }
+
+ if (object == NULL)
+ {
+ if (id)
+ g_printerr ("No object with ID '%s' found\n", id);
+ else
+ g_printerr ("No previewable object found\n");
+ exit (1);
+ }
+
+ if (!GTK_IS_WIDGET (object))
+ {
+ g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
+ exit (1);
+ }
+
+ if (GTK_IS_WINDOW (object))
+ window = GTK_WIDGET (object);
+ else
+ {
+ GtkWidget *widget = GTK_WIDGET (object);
+
+ window = gtk_window_new ();
+
+ if (GTK_IS_BUILDABLE (object))
+ id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
+
+ set_window_title (GTK_WINDOW (window), filename, id);
+
+ g_object_ref (widget);
+ if (gtk_widget_get_parent (widget) != NULL)
+ gtk_box_remove (GTK_BOX (gtk_widget_get_parent (widget)), widget);
+ gtk_window_set_child (GTK_WINDOW (window), widget);
+ g_object_unref (widget);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+ g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
+
+ while (!done)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_object_unref (builder);
+}
+
+void
+do_preview (int *argc,
+ const char ***argv)
+{
+ GOptionContext *context;
+ char *id = NULL;
+ char *css = NULL;
+ char **filenames = NULL;
+ const GOptionEntry entries[] = {
+ { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
+ { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+ { NULL, }
+ };
+ GError *error = NULL;
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (!g_option_context_parse (context, argc, (char ***)argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ g_option_context_free (context);
+
+ if (filenames == NULL)
+ {
+ g_printerr ("No .ui file specified\n");
+ exit (1);
+ }
+
+ if (g_strv_length (filenames) > 1)
+ {
+ g_printerr ("Can only preview a single .ui file\n");
+ exit (1);
+ }
+
+ preview_file (filenames[0], id, css);
+
+ g_strfreev (filenames);
+ g_free (id);
+ g_free (css);
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+typedef struct Element Element;
+struct Element {
+ Element *parent;
+ char *element_name;
+ char **attribute_names;
+ char **attribute_values;
+ char *data;
+ GList *children;
+
+ int line_number;
+ int char_number;
+};
+
+static void
+free_element (gpointer data)
+{
+ Element *element = data;
+ g_list_free_full (element->children, free_element);
+ g_free (element->element_name);
+ g_strfreev (element->attribute_names);
+ g_strfreev (element->attribute_values);
+ g_free (element->data);
+ g_free (element);
+}
+
+typedef struct {
+ Element *root;
+ Element *current;
+ GString *value;
+ GtkBuilder *builder;
+ const char *input_filename;
+ char *output_filename;
+ FILE *output;
+ gboolean convert3to4;
+ gboolean has_gtk_requires;
+} MyParserData;
+
+static void
+start_element (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+ Element *elt;
+
+ elt = g_new0 (Element, 1);
+ elt->parent = data->current;
+ elt->element_name = g_strdup (element_name);
+ elt->attribute_names = g_strdupv ((char **)attribute_names);
+ elt->attribute_values = g_strdupv ((char **)attribute_values);
+
+ g_markup_parse_context_get_position (context, &elt->line_number, &elt->char_number);
+
+ if (data->current)
+ data->current->children = g_list_append (data->current->children, elt);
+ data->current = elt;
+
+ if (data->root == NULL)
+ data->root = elt;
+
+ g_string_truncate (data->value, 0);
+}
+
+static void
+end_element (GMarkupParseContext *context,
+ const char *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+
+ data->current->data = g_strdup (data->value->str);
+
+ data->current = data->current->parent;
+}
+
+static void
+text (GMarkupParseContext *context,
+ const char *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+
+ if (data->value)
+ {
+ g_string_append_len (data->value, text, text_len);
+ return;
+ }
+}
+
+static GMarkupParser parser = {
+ start_element,
+ end_element,
+ text,
+ NULL,
+ NULL
+};
+
+static const char *
+canonical_boolean_value (MyParserData *data,
+ const char *string)
+{
+ GValue value = G_VALUE_INIT;
+ gboolean b = FALSE;
+
+ if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
+ b = g_value_get_boolean (&value);
+
+ return b ? "1" : "0";
+}
+
+typedef enum {
+ PROP_KIND_OBJECT,
+ PROP_KIND_PACKING,
+ PROP_KIND_CELL_PACKING,
+ PROP_KIND_LAYOUT
+} PropKind;
+
+static PropKind
+get_prop_kind (Element *element)
+{
+ g_assert (g_str_equal (element->element_name, "property"));
+
+ if (g_str_equal (element->parent->element_name, "packing"))
+ return PROP_KIND_PACKING;
+ else if (g_str_equal (element->parent->element_name, "layout"))
+ return PROP_KIND_LAYOUT;
+ else if (g_str_equal (element->parent->element_name, "cell-packing"))
+ return PROP_KIND_CELL_PACKING;
+ else
+ return PROP_KIND_OBJECT;
+}
+
+/* A number of properties unfortunately can't be omitted even
+ * if they are nominally set to their default value. In many
+ * cases, this is due to subclasses not overriding the default
+ * value from the superclass.
+ */
+static gboolean
+needs_explicit_setting (GParamSpec *pspec,
+ PropKind kind)
+{
+ struct _Prop {
+ const char *class;
+ const char *property;
+ PropKind kind;
+ } props[] = {
+ { "GtkAboutDialog", "program-name", PROP_KIND_OBJECT },
+ { "GtkCalendar", "year", PROP_KIND_OBJECT },
+ { "GtkCalendar", "month", PROP_KIND_OBJECT },
+ { "GtkCalendar", "day", PROP_KIND_OBJECT },
+ { "GtkPlacesSidebar", "show-desktop", PROP_KIND_OBJECT },
+ { "GtkRadioButton", "draw-indicator", PROP_KIND_OBJECT },
+ { "GtkWidget", "hexpand", PROP_KIND_OBJECT },
+ { "GtkWidget", "vexpand", PROP_KIND_OBJECT },
+ { "GtkGridLayoutChild", "row", PROP_KIND_LAYOUT },
+ { "GtkGridLayoutChild", "column", PROP_KIND_LAYOUT },
+ };
+ gboolean found;
+ int k;
+ const char *class_name;
+
+ class_name = g_type_name (pspec->owner_type);
+
+ found = FALSE;
+ for (k = 0; k < G_N_ELEMENTS (props); k++)
+ {
+ if (strcmp (class_name, props[k].class) == 0 &&
+ strcmp (pspec->name, props[k].property) == 0 &&
+ kind == props[k].kind)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static gboolean
+has_attribute (Element *elt,
+ const char *name,
+ const char *value)
+{
+ int i;
+
+ for (i = 0; elt->attribute_names[i]; i++)
+ {
+ if (strcmp (elt->attribute_names[i], name) == 0 &&
+ (value == NULL || strcmp (elt->attribute_values[i], value) == 0))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_cdata_property (Element *element)
+{
+ if (g_str_equal (element->element_name, "property") &&
+ has_attribute (element, "name", "bytes") &&
+ element->parent != NULL &&
+ g_str_equal (element->parent->element_name, "object") &&
+ has_attribute (element->parent, "class", "GtkBuilderListItemFactory"))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+is_pcdata_element (Element *element)
+{
+ /* elements that can contain text */
+ const char *names[] = {
+ "property",
+ "attribute",
+ "action-widget",
+ "pattern",
+ "mime-type",
+ "col",
+ "item",
+ "mark",
+ NULL,
+ };
+
+ if (g_str_equal (element->element_name, "property") &&
+ (g_strv_contains ((const char * const *)element->attribute_names, "bind-source") ||
+ g_strv_contains ((const char * const *)element->attribute_names, "bind_source")))
+ return FALSE;
+
+ if (g_strv_contains (names, element->element_name))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+is_container_element (Element *element)
+{
+ /* elements that just hold a list of things and
+ * can be omitted when they have no children
+ */
+ const char *names[] = {
+ "packing",
+ "layout",
+ "cell-packing",
+ "attributes",
+ "action-widgets",
+ "patterns",
+ "mime-types",
+ "attributes",
+ "row",
+ "items",
+ NULL
+ };
+
+ if (g_strv_contains (names, element->element_name))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+canonicalize_key (char *key)
+{
+ char *p;
+
+ for (p = key; *p != 0; p++)
+ {
+ char c = *p;
+
+ /* We may meet something like AtkObject::accessible-name */
+ if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
+ continue;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+static struct {
+ const char *class;
+ const char *layout_manager;
+} layout_managers[] = {
+ { "GtkBox", "GtkBoxLayout" },
+ { "GtkGrid", "GtkGridLayout" },
+ { "GtkFixed", "GtkFixedLayout" },
+ { "GtkFileChooserButton", "GtkBinLayout" },
+ { "GtkFileChooserWidget", "GtkBinLayout" },
+ { "GtkOverlay", "GtkOverlayLayout" }
+};
+
+static GParamSpec *
+get_property_pspec (MyParserData *data,
+ const char *class_name,
+ const char *property_name,
+ PropKind kind)
+{
+ GType type;
+ GObjectClass *class;
+ GParamSpec *pspec;
+ char *canonical_name;
+
+ type = g_type_from_name (class_name);
+ if (type == G_TYPE_INVALID)
+ {
+ type = gtk_builder_get_type_from_name (data->builder, class_name);
+ if (type == G_TYPE_INVALID)
+ return NULL;
+ }
+
+ class = g_type_class_ref (type);
+ canonical_name = g_strdup (property_name);
+ canonicalize_key (canonical_name);
+ switch (kind)
+ {
+ case PROP_KIND_OBJECT:
+ pspec = g_object_class_find_property (class, canonical_name);
+ break;
+
+ case PROP_KIND_PACKING:
+ pspec = NULL;
+ break;
+
+ case PROP_KIND_CELL_PACKING:
+ {
+ GObjectClass *cell_class;
+
+ /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
+ cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
+ pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
+ g_type_class_unref (cell_class);
+ }
+ break;
+
+ case PROP_KIND_LAYOUT:
+ {
+ int i;
+ const char *layout_manager = NULL;
+
+ pspec = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS (layout_managers); i++)
+ {
+ if (g_str_equal (layout_managers[i].class, class_name))
+ {
+ layout_manager = layout_managers[i].layout_manager;
+ break;
+ }
+ }
+
+ if (layout_manager)
+ {
+ GtkLayoutManagerClass *layout_manager_class;
+
+ layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (g_type_class_ref (g_type_from_name (layout_manager)));
+ if (layout_manager_class->layout_child_type != G_TYPE_INVALID)
+ {
+ GObjectClass *layout_child_class;
+ layout_child_class = g_type_class_ref (layout_manager_class->layout_child_type);
+ pspec = g_object_class_find_property (layout_child_class, canonical_name);
+ g_type_class_unref (layout_child_class);
+ }
+ g_type_class_unref (layout_manager_class);
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ g_free (canonical_name);
+ g_type_class_unref (class);
+
+ return pspec;
+}
+
+static const char *get_class_name (Element *element);
+
+static gboolean
+value_is_default (Element *element,
+ MyParserData *data,
+ GParamSpec *pspec,
+ const char *value_string)
+{
+ GValue value = { 0, };
+ gboolean ret;
+ GError *error = NULL;
+
+ if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
+ return FALSE;
+
+ if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_BOXED))
+ return FALSE;
+
+ if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
+ {
+ g_printerr (_("%s:%d: Couldn’t parse value for property '%s': %s\n"), data->input_filename, element->line_number, pspec->name, error->message);
+ g_error_free (error);
+ ret = FALSE;
+ }
+ else
+ {
+ /* GtkWidget::visible has a 'smart' default */
+ if (pspec->owner_type == GTK_TYPE_WIDGET &&
+ g_str_equal (pspec->name, "visible"))
+ {
+ const char *class_name = get_class_name (element);
+ GType type = g_type_from_name (class_name);
+ gboolean default_value;
+
+ if (g_type_is_a (type, GTK_TYPE_ROOT) ||
+ g_type_is_a (type, GTK_TYPE_POPOVER))
+ default_value = FALSE;
+ else
+ default_value = TRUE;
+
+ ret = g_value_get_boolean (&value) == default_value;
+ }
+ else if (pspec->owner_type == GTK_TYPE_WINDOW &&
+ (g_str_equal (pspec->name, "default-width") ||
+ g_str_equal (pspec->name, "default-height")))
+ {
+ int default_size;
+
+ default_size = g_value_get_int (&value);
+ ret = default_size <= 0;
+ }
+ else
+ ret = g_param_value_defaults (pspec, &value);
+ }
+
+ g_value_reset (&value);
+
+ return ret;
+}
+
+static const char *
+get_attribute_value (Element *element,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (g_str_equal (element->attribute_names[i], name))
+ return element->attribute_values[i];
+ }
+
+ return NULL;
+}
+
+static void
+set_attribute_value (Element *element,
+ const char *name,
+ const char *value)
+{
+ int i;
+ int len;
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (g_str_equal (element->attribute_names[i], name))
+ {
+ g_free (element->attribute_values[i]);
+ element->attribute_values[i] = g_strdup (value);
+ return;
+ }
+ }
+
+ len = g_strv_length (element->attribute_names);
+ element->attribute_names = g_realloc_n (element->attribute_names, len + 2, sizeof (char *));
+ element->attribute_values = g_realloc_n (element->attribute_values, len + 2, sizeof (char *));
+ element->attribute_names[len] = g_strdup (name);
+ element->attribute_values[len] = g_strdup (value);
+ element->attribute_names[len + 1] = NULL;
+ element->attribute_values[len + 1] = NULL;
+}
+
+static gboolean
+element_is_object_or_template (Element *element)
+{
+ return g_str_equal (element->element_name, "object") ||
+ g_str_equal (element->element_name, "template");
+}
+
+static const char *
+get_class_name (Element *element)
+{
+ Element *parent = element->parent;
+
+ if (element_is_object_or_template (element))
+ parent = element;
+
+ if (g_str_equal (parent->element_name, "packing"))
+ parent = parent->parent->parent; /* child - object */
+ else if (g_str_equal (parent->element_name, "layout"))
+ parent = parent->parent->parent->parent; /* object - child - object */
+
+
+ if (g_str_equal (parent->element_name, "object"))
+ {
+ return get_attribute_value (parent, "class");
+ }
+ else if (g_str_equal (parent->element_name, "template"))
+ {
+ if (get_attribute_value (parent, "parent"))
+ return get_attribute_value (parent, "parent");
+ else
+ return get_attribute_value (parent, "class");
+ }
+
+ return NULL;
+}
+
+static gboolean
+property_is_boolean (Element *element,
+ MyParserData *data)
+{
+ GParamSpec *pspec = NULL;
+ const char *class_name;
+ const char *property_name;
+ int i;
+ PropKind kind;
+
+ kind = get_prop_kind (element);
+ class_name = get_class_name (element);
+ property_name = "";
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (strcmp (element->attribute_names[i], "name") == 0)
+ property_name = (const char *)element->attribute_values[i];
+ }
+
+ if (class_name && property_name)
+ pspec = get_property_pspec (data, class_name, property_name, kind);
+ if (pspec)
+ return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
+
+ return FALSE;
+}
+
+static void
+warn_missing_property (Element *element,
+ MyParserData *data,
+ const char *class_name,
+ const char *property_name,
+ PropKind kind)
+{
+ const char *kind_str[] = { "", "Packing ", "Cell ", "Layout " };
+
+ g_printerr (_("%s:%d: %sproperty %s::%s not found\n"),
+ data->input_filename, element->line_number, kind_str[kind], class_name, property_name);
+}
+
+static gboolean
+property_can_be_omitted (Element *element,
+ MyParserData *data)
+{
+ int i;
+ gboolean bound;
+ gboolean translatable;
+ const char *class_name;
+ const char *property_name;
+ const char *value_string;
+ GParamSpec *pspec;
+ PropKind kind;
+
+ kind = get_prop_kind (element);
+ class_name = get_class_name (element);
+ property_name = "";
+ value_string = element->data;
+
+ bound = FALSE;
+ translatable = FALSE;
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (strcmp (element->attribute_names[i], "bind-source") == 0 ||
+ strcmp (element->attribute_names[i], "bind_source") == 0)
+ bound = TRUE;
+ else if (strcmp (element->attribute_names[i], "translatable") == 0)
+ translatable = TRUE;
+ else if (strcmp (element->attribute_names[i], "name") == 0)
+ property_name = (const char *)element->attribute_values[i];
+ }
+
+ if (translatable)
+ return FALSE;
+
+ if (bound)
+ return FALSE;
+
+ pspec = get_property_pspec (data, class_name, property_name, kind);
+ if (pspec == NULL)
+ {
+ warn_missing_property (element, data, class_name, property_name, kind);
+ return FALSE;
+ }
+
+ if (needs_explicit_setting (pspec, kind))
+ return FALSE;
+
+ return value_is_default (element, data, pspec, value_string);
+}
+
+static gboolean
+property_has_been_removed (Element *element,
+ MyParserData *data)
+{
+ const char *class_name;
+ const char *property_name;
+ struct _Prop {
+ const char *class;
+ const char *property;
+ PropKind kind;
+ } props[] = {
+ { "GtkActionBar", "position", PROP_KIND_PACKING },
+ { "GtkButtonBox", "secondary", PROP_KIND_PACKING },
+ { "GtkButtonBox", "non-homogeneous", PROP_KIND_PACKING },
+ { "GtkBox", "position", PROP_KIND_PACKING },
+ { "GtkBox", "pack-type", PROP_KIND_PACKING },
+ { "GtkHeaderBar", "position", PROP_KIND_PACKING },
+ { "GtkPopoverMenu", "position",PROP_KIND_PACKING },
+ { "GtkCheckButton", "draw-indicator", PROP_KIND_OBJECT },
+ };
+ char *canonical_name;
+ gboolean found;
+ int i, k;
+ PropKind kind;
+
+ kind = get_prop_kind (element);
+
+ class_name = get_class_name (element);
+ property_name = "";
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (strcmp (element->attribute_names[i], "name") == 0)
+ property_name = (const char *)element->attribute_values[i];
+ }
+
+ canonical_name = g_strdup (property_name);
+ g_strdelimit (canonical_name, "_", '-');
+
+ found = FALSE;
+ for (k = 0; k < G_N_ELEMENTS (props); k++)
+ {
+ if (strcmp (class_name, props[k].class) == 0 &&
+ strcmp (canonical_name, props[k].property) == 0 &&
+ kind == props[k].kind)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_free (canonical_name);
+
+ return found;
+}
+
+static void
+maybe_rename_property (Element *element, MyParserData *data)
+{
+ const char *class_name;
+ const char *property_name;
+ struct _Prop {
+ const char *class;
+ const char *property;
+ GType type;
+ PropKind kind;
+ const char *new_name;
+ const char *alt_names[3];
+ } props[] = {
+ /* the "replacement" property is placed *after* the "added" properties */
+ { "GtkPopover", "modal", GTK_TYPE_POPOVER, PROP_KIND_OBJECT, "autohide", { NULL, NULL, NULL } },
+ { "GtkWidget", "expand", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "vexpand", { "hexpand", NULL, NULL } },
+ { "GtkWidget", "margin", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-bottom", { "margin-start", "margin-end", "margin-top" } },
+ { "GtkWidget", "margin-left", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-start", { NULL, NULL, NULL } },
+ { "GtkWidget", "margin-right", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-end", { NULL, NULL, NULL } },
+ { "GtkHeaderBar", "show-close-button", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "show-title-buttons", { NULL, NULL, NULL } },
+ { "GtkHeaderBar", "custom-title", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "title-widget", { NULL, NULL, NULL } },
+ { "GtkStack", "homogeneous", GTK_TYPE_STACK, PROP_KIND_OBJECT, "hhomogeneous", { "vhomogeneous", NULL, NULL } },
+ { "GtkImage", "pixbuf", GTK_TYPE_IMAGE, PROP_KIND_OBJECT, "file", { NULL, NULL, NULL } },
+ };
+ int i, k, l;
+ PropKind kind;
+ int prop_name_index = 0;
+ GType type;
+ char *canonical_name;
+
+ kind = get_prop_kind (element);
+
+ class_name = get_class_name (element);
+ property_name = NULL;
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ if (strcmp (element->attribute_names[i], "name") == 0)
+ {
+ prop_name_index = i;
+ property_name = (const char *)element->attribute_values[i];
+ }
+ }
+
+ if (property_name == NULL)
+ return;
+
+ type = g_type_from_name (class_name);
+
+ canonical_name = g_strdup (property_name);
+ g_strdelimit (canonical_name, "_", '-');
+
+ for (k = 0; k < G_N_ELEMENTS (props); k++)
+ {
+ if (g_type_is_a (type, props[k].type) &&
+ strcmp (canonical_name, props[k].property) == 0 &&
+ kind == props[k].kind)
+ {
+ g_free (element->attribute_values[prop_name_index]);
+ element->attribute_values[prop_name_index] = g_strdup (props[k].new_name);
+ for (l = 0; l < 3 && props[k].alt_names[l]; l++)
+ {
+ Element *elt;
+ GList *sibling;
+
+ elt = g_new0 (Element, 1);
+ elt->parent = element->parent;
+ elt->element_name = g_strdup (element->element_name);
+ elt->attribute_names = g_strdupv ((char **) element->attribute_names);
+ elt->attribute_values = g_strdupv ((char **) element->attribute_values);
+ elt->data = g_strdup (element->data);
+
+ g_free (elt->attribute_values[prop_name_index]);
+ elt->attribute_values[prop_name_index] = g_strdup (props[k].alt_names[l]);
+
+ sibling = g_list_find (element->parent->children, element);
+ element->parent->children = g_list_insert_before (element->parent->children,
+ sibling,
+ elt);
+ }
+ break;
+ }
+ }
+
+ g_free (canonical_name);
+}
+
+static Element *
+rewrite_stack_child (Element *child, MyParserData *data)
+{
+ Element *object = NULL;
+ Element *packing = NULL;
+ Element *new_object;
+ Element *prop;
+ GList *l;
+
+ if (!g_str_equal (child->element_name, "child"))
+ return child;
+
+ for (l = child->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+ if (g_str_equal (elt->element_name, "object"))
+ object = elt;
+ else if (g_str_equal (elt->element_name, "packing"))
+ packing = elt;
+ }
+
+ if (!packing)
+ return child;
+
+ new_object = g_new0 (Element, 1);
+ new_object->element_name = g_strdup ("object");
+ new_object->attribute_names = g_new0 (char *, 2);
+ new_object->attribute_names[0] = g_strdup ("class");
+ new_object->attribute_values = g_new0 (char *, 2);
+ new_object->attribute_values[0] = g_strdup ("GtkStackPage");
+ new_object->children = packing->children;
+ new_object->parent = child;
+ packing->children = NULL;
+
+ prop = g_new0 (Element, 1);
+ prop->element_name = g_strdup ("property");
+ prop->attribute_names = g_new0 (char *, 2);
+ prop->attribute_names[0] = g_strdup ("name");
+ prop->attribute_values = g_new0 (char *, 2);
+ prop->attribute_values[0] = g_strdup ("child");
+ prop->children = g_list_append (prop->children, object);
+ prop->parent = new_object;
+ new_object->children = g_list_append (new_object->children, prop);
+
+ g_list_free (child->children);
+ child->children = g_list_append (NULL, new_object);
+
+ return child;
+}
+
+static void
+rewrite_stack (Element *element,
+ MyParserData *data)
+{
+ GList *l, *new_children;
+
+ new_children = NULL;
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ new_children = g_list_append (new_children, rewrite_stack_child (child, data));
+ }
+
+ g_list_free (element->children);
+ element->children = new_children;
+}
+
+static Element *
+rewrite_assistant_child (Element *child, MyParserData *data)
+{
+ Element *object = NULL;
+ Element *packing = NULL;
+ Element *new_object;
+ Element *prop;
+ GList *l;
+
+ if (!g_str_equal (child->element_name, "child"))
+ return child;
+
+ for (l = child->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+ if (g_str_equal (elt->element_name, "object"))
+ object = elt;
+ else if (g_str_equal (elt->element_name, "packing"))
+ packing = elt;
+ }
+
+ if (!packing)
+ return child;
+
+ new_object = g_new0 (Element, 1);
+ new_object->element_name = g_strdup ("object");
+ new_object->attribute_names = g_new0 (char *, 2);
+ new_object->attribute_names[0] = g_strdup ("class");
+ new_object->attribute_values = g_new0 (char *, 2);
+ new_object->attribute_values[0] = g_strdup ("GtkAssistantPage");
+ new_object->children = packing->children;
+ new_object->parent = child;
+ packing->children = NULL;
+
+ prop = g_new0 (Element, 1);
+ prop->element_name = g_strdup ("property");
+ prop->attribute_names = g_new0 (char *, 2);
+ prop->attribute_names[0] = g_strdup ("name");
+ prop->attribute_values = g_new0 (char *, 2);
+ prop->attribute_values[0] = g_strdup ("child");
+ prop->children = g_list_append (prop->children, object);
+ prop->parent = new_object;
+ new_object->children = g_list_append (new_object->children, prop);
+
+ g_list_free (child->children);
+ child->children = g_list_append (NULL, new_object);
+
+ return child;
+}
+
+static void
+rewrite_assistant (Element *element,
+ MyParserData *data)
+{
+ GList *l, *new_children;
+
+ new_children = NULL;
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ new_children = g_list_append (new_children, rewrite_assistant_child (child, data));
+ }
+
+ g_list_free (element->children);
+ element->children = new_children;
+}
+
+static Element *
+rewrite_notebook_page (Element *child, Element *tab, MyParserData *data)
+{
+ Element *object = NULL;
+ Element *tab_obj = NULL;
+ Element *packing = NULL;
+ Element *new_object;
+ Element *prop;
+ GList *l;
+
+ if (!g_str_equal (child->element_name, "child"))
+ return child;
+
+ if (has_attribute (child, "type", "tab") ||
+ has_attribute (child, "type", "action-start") ||
+ has_attribute (child, "type", "action-end"))
+ return child;
+
+ for (l = child->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+ if (g_str_equal (elt->element_name, "object"))
+ object = elt;
+ else if (g_str_equal (elt->element_name, "packing"))
+ packing = elt;
+ }
+
+ if (!packing && !tab)
+ return child;
+
+ if (tab)
+ {
+ for (l = tab->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+ if (g_str_equal (elt->element_name, "object"))
+ tab_obj = elt;
+ }
+ }
+
+ new_object = g_new0 (Element, 1);
+ new_object->element_name = g_strdup ("object");
+ new_object->attribute_names = g_new0 (char *, 2);
+ new_object->attribute_names[0] = g_strdup ("class");
+ new_object->attribute_values = g_new0 (char *, 2);
+ new_object->attribute_values[0] = g_strdup ("GtkNotebookPage");
+ new_object->parent = child;
+ if (packing)
+ {
+ new_object->children = packing->children;
+ packing->children = NULL;
+ }
+
+ prop = g_new0 (Element, 1);
+ prop->element_name = g_strdup ("property");
+ prop->attribute_names = g_new0 (char *, 2);
+ prop->attribute_names[0] = g_strdup ("name");
+ prop->attribute_values = g_new0 (char *, 2);
+ prop->attribute_values[0] = g_strdup ("child");
+ prop->children = g_list_append (prop->children, object);
+ prop->parent = new_object;
+ new_object->children = g_list_append (new_object->children, prop);
+
+ if (tab_obj)
+ {
+ prop = g_new0 (Element, 1);
+ prop->element_name = g_strdup ("property");
+ prop->attribute_names = g_new0 (char *, 2);
+ prop->attribute_names[0] = g_strdup ("name");
+ prop->attribute_values = g_new0 (char *, 2);
+ prop->attribute_values[0] = g_strdup ("tab");
+ prop->children = g_list_append (prop->children, tab_obj);
+ prop->parent = new_object;
+ new_object->children = g_list_append (new_object->children, prop);
+ }
+
+ g_list_free (child->children);
+ child->children = g_list_append (NULL, new_object);
+
+ return child;
+}
+
+static void
+rewrite_notebook (Element *element,
+ MyParserData *data)
+{
+ GList *l, *new_children;
+
+ new_children = NULL;
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ Element *tab = l->next ? l->next->data : NULL;
+
+ if (tab && has_attribute (tab, "type", "tab"))
+ {
+ new_children = g_list_append (new_children, rewrite_notebook_page (child, tab, data));
+ l = l->next; /* skip the tab */
+ }
+ else
+ new_children = g_list_append (new_children, rewrite_notebook_page (child, NULL, data));
+ }
+
+ g_list_free (element->children);
+ element->children = new_children;
+}
+
+static void
+rewrite_pack_type_child (Element *element,
+ MyParserData *data)
+{
+ Element *pack_type = NULL;
+ GList *l, *ll;
+
+ if (!g_str_equal (element->element_name, "child"))
+ return;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "packing"))
+ {
+ for (ll = elt->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "property") &&
+ has_attribute (elt2, "name", "pack-type"))
+ {
+ pack_type = elt2;
+ elt->children = g_list_remove (elt->children, pack_type);
+ if (elt->children == NULL)
+ {
+ element->children = g_list_remove (element->children, elt);
+ free_element (elt);
+ }
+ break;
+ }
+ }
+ }
+
+ if (pack_type)
+ break;
+ }
+
+ if (pack_type)
+ {
+ char **attnames = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
+ char **attvalues = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
+ int i;
+
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ attnames[i] = g_strdup (element->attribute_names[i]);
+ attvalues[i] = g_strdup (element->attribute_values[i]);
+ }
+
+ attnames[i] = g_strdup ("type");
+ attvalues[i] = g_strdup (pack_type->data);
+
+ g_strfreev (element->attribute_names);
+ g_strfreev (element->attribute_values);
+
+ element->attribute_names = attnames;
+ element->attribute_values = attvalues;
+
+ free_element (pack_type);
+ }
+}
+
+static void
+rewrite_pack_type (Element *element,
+ MyParserData *data)
+{
+ GList *l;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+ if (g_str_equal (elt->element_name, "child"))
+ rewrite_pack_type_child (elt, data);
+ }
+}
+
+static void
+rewrite_paned_child (Element *element,
+ MyParserData *data,
+ Element *child,
+ const char *suffix)
+{
+ Element *resize = NULL;
+ Element *shrink = NULL;
+ GList *l, *ll;
+
+ for (l = child->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "packing"))
+ {
+ for (ll = elt->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "property") &&
+ has_attribute (elt2, "name", "resize"))
+ resize = elt2;
+ else if (g_str_equal (elt2->element_name, "property") &&
+ has_attribute (elt2, "name", "shrink"))
+ shrink = elt2;
+ }
+ if (resize)
+ elt->children = g_list_remove (elt->children, resize);
+ if (shrink)
+ elt->children = g_list_remove (elt->children, shrink);
+ if (elt->children == NULL)
+ {
+ child->children = g_list_remove (child->children, elt);
+ free_element (elt);
+ }
+ }
+
+ if (resize || shrink)
+ break;
+ }
+
+ if (resize)
+ {
+ Element *elt;
+
+ elt = g_new0 (Element, 1);
+ elt->parent = element;
+ elt->element_name = g_strdup ("property");
+ elt->attribute_names = g_new0 (char *, 2);
+ elt->attribute_names[0] = g_strdup ("name");
+ elt->attribute_values = g_new0 (char *, 2);
+ elt->attribute_values[0] = g_strconcat ("resize-", suffix, NULL);
+ elt->data = g_strdup (resize->data);
+
+ element->children = g_list_prepend (element->children, elt);
+
+ free_element (resize);
+ }
+
+ if (shrink)
+ {
+ Element *elt;
+
+ elt = g_new0 (Element, 1);
+ elt->parent = element;
+ elt->element_name = g_strdup ("property");
+ elt->attribute_names = g_new0 (char *, 2);
+ elt->attribute_names[0] = g_strdup ("name");
+ elt->attribute_values = g_new0 (char *, 2);
+ elt->attribute_values[0] = g_strconcat ("shrink-", suffix, NULL);
+ elt->data = g_strdup (shrink->data);
+
+ element->children = g_list_prepend (element->children, elt);
+
+ free_element (shrink);
+ }
+}
+
+static void
+rewrite_paned (Element *element,
+ MyParserData *data)
+{
+ Element *child1 = NULL;
+ Element *child2 = NULL;
+ GList *l;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "child"))
+ {
+ if (child1 == NULL)
+ child1 = elt;
+ else if (child2 == NULL)
+ child2 = elt;
+ else
+ break;
+ }
+ }
+
+ if (child1)
+ rewrite_paned_child (element, data, child1, "start-child");
+
+ if (child2)
+ rewrite_paned_child (element, data, child2, "end-child");
+}
+
+static void
+rewrite_dialog (Element *element,
+ MyParserData *data)
+{
+ Element *content_area = NULL;
+ Element *vbox = NULL;
+ Element *action_area = NULL;
+ GList *l;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "child") &&
+ g_strcmp0 (get_attribute_value (elt, "internal-child"), "vbox") == 0)
+ {
+ content_area = elt;
+ break;
+ }
+ }
+
+ if (!content_area || !content_area->children)
+ return;
+
+ vbox = content_area->children->data;
+
+ for (l = vbox->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "child") &&
+ g_strcmp0 (get_attribute_value (elt, "internal-child"), "action_area") == 0)
+ {
+ action_area = elt;
+ break;
+ }
+ }
+
+ if (!action_area)
+ return;
+
+ set_attribute_value (content_area, "internal-child", "content_area");
+ vbox->children = g_list_remove (vbox->children, action_area);
+ action_area->parent = element;
+ element->children = g_list_append (element->children, action_area);
+
+ for (l = action_area->children; l; l = l->next)
+ {
+ Element *elt = l->data;
+
+ if (g_str_equal (elt->element_name, "packing"))
+ {
+ action_area->children = g_list_remove (action_area->children, elt);
+ free_element (elt);
+ break;
+ }
+ }
+
+}
+
+static void
+rewrite_grid_layout_prop (Element *element,
+ const char *attr_name,
+ const char *old_value,
+ const char *new_value)
+{
+ if (g_str_equal (element->element_name, "property"))
+ {
+ char *canonical_name;
+
+ canonical_name = g_strdup (old_value);
+ g_strdelimit (canonical_name, "_", '-');
+
+ if (has_attribute (element, attr_name, old_value) ||
+ has_attribute (element, attr_name, canonical_name))
+ {
+ set_attribute_value (element, attr_name, new_value);
+ }
+
+ g_free (canonical_name);
+ }
+}
+
+static void
+rewrite_grid_layout (Element *element,
+ MyParserData *data)
+{
+ struct _Prop {
+ const char *attr_name;
+ const char *old_value;
+ const char *new_value;
+ } props[] = {
+ { "name", "left_attach", "column", },
+ { "name", "top_attach", "row", },
+ { "name", "width", "column-span", },
+ { "name", "height", "row-span", },
+ };
+ GList *l, *ll;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "child"))
+ {
+ Element *object = NULL;
+ Element *packing = NULL;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "object"))
+ object = elt2;
+
+ if (g_str_equal (elt2->element_name, "packing"))
+ packing = elt2;
+ }
+
+ if (object && packing)
+ {
+ int i;
+
+ child->children = g_list_remove (child->children, packing);
+
+ g_free (packing->element_name);
+ packing->element_name = g_strdup ("layout");
+
+ packing->parent = object;
+ object->children = g_list_append (object->children, packing);
+
+ for (ll = packing->children; ll; ll = ll->next)
+ {
+ Element *elt = ll->data;
+
+ for (i = 0; i < G_N_ELEMENTS (props); i++)
+ rewrite_grid_layout_prop (elt,
+ props[i].attr_name,
+ props[i].old_value,
+ props[i].new_value);
+ }
+ }
+ }
+ }
+}
+
+static Element *
+add_element (Element *parent,
+ const char *element_name)
+{
+ Element *child;
+
+ child = g_new0 (Element, 1);
+ child->parent = parent;
+ child->element_name = g_strdup (element_name);
+ child->attribute_names = g_new0 (char *, 1);
+ child->attribute_values = g_new0 (char *, 1);
+ parent->children = g_list_prepend (parent->children, child);
+
+ return child;
+}
+
+static Element *
+write_box_prop (Element *element,
+ Element *parent,
+ const char *name,
+ const char *value)
+{
+
+ if (element)
+ g_free (element->data);
+ else
+ {
+ element = add_element (parent, "property");
+ set_attribute_value (element, "name", name);
+ }
+ element->data = g_strdup (value);
+
+ return element;
+}
+
+static void
+rewrite_box (Element *element,
+ MyParserData *data)
+{
+ GList *l, *ll;
+ GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ if (g_str_equal (get_class_name (element), "GtkVBox"))
+ write_box_prop (NULL, element, "orientation", "vertical");
+
+ if (!g_str_equal (get_class_name (element), "GtkBox"))
+ set_attribute_value (element, "class", "GtkBox");
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "property"))
+ {
+ if (has_attribute (child, "name", "orientation"))
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (gtk_builder_value_from_string_type (data->builder,
+ GTK_TYPE_ORIENTATION,
+ child->data,
+ &value,
+ NULL))
+ orientation = g_value_get_enum (&value);
+ }
+ }
+ }
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ if (g_str_equal (child->element_name, "child"))
+ {
+ Element *object = NULL;
+ Element *packing = NULL;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "object"))
+ object = elt2;
+
+ if (g_str_equal (elt2->element_name, "packing"))
+ packing = elt2;
+ }
+
+ if (object && packing)
+ {
+ Element *halign = NULL;
+ Element *hexpand = NULL;
+ Element *valign = NULL;
+ Element *vexpand = NULL;
+
+ gboolean expand = FALSE;
+ gboolean fill = TRUE;
+
+ for (ll = object->children; ll; ll = ll->next)
+ {
+ Element *elt = ll->data;
+ if (g_str_equal (elt->element_name, "property"))
+ {
+ if (has_attribute (elt, "name", "halign"))
+ halign = elt;
+ else if (has_attribute (elt, "name", "hexpand"))
+ hexpand = elt;
+ else if (has_attribute (elt, "name", "valign"))
+ valign = elt;
+ else if (has_attribute (elt, "name", "vexpand"))
+ vexpand = elt;
+ }
+ }
+
+ for (ll = packing->children; ll; ll = ll->next)
+ {
+ Element *elt = ll->data;
+
+ if (has_attribute (elt, "name", "expand"))
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (gtk_builder_value_from_string_type (data->builder,
+ G_TYPE_BOOLEAN,
+ elt->data,
+ &value,
+ NULL))
+ expand = g_value_get_boolean (&value);
+ }
+
+ if (has_attribute (elt, "name", "fill"))
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (gtk_builder_value_from_string_type (data->builder,
+ G_TYPE_BOOLEAN,
+ elt->data,
+ &value,
+ NULL))
+ fill = g_value_get_boolean (&value);
+ }
+ }
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (expand)
+ hexpand = write_box_prop (hexpand, object, "hexpand", "1");
+ if (!fill)
+ halign = write_box_prop (halign, object, "halign", "center");
+ }
+ else if (orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ if (expand)
+ vexpand = write_box_prop (vexpand, object, "vexpand", "1");
+ if (!fill)
+ valign = write_box_prop (valign, object, "valign", "center");
+ }
+
+ child->children = g_list_remove (child->children, packing);
+ free_element (packing);
+ }
+ }
+ }
+}
+
+static void
+rewrite_bin_child (Element *element,
+ MyParserData *data)
+{
+ GList *l, *ll;
+ const char *class_name;
+ GType type;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ Element *object = NULL;
+
+ if (!g_str_equal (child->element_name, "child") ||
+ has_attribute (child, "type", NULL))
+ continue;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elem = ll->data;
+
+ if (!g_str_equal (elem->element_name, "object"))
+ continue;
+
+ class_name = get_attribute_value (elem, "class");
+ if (!class_name)
+ continue;
+
+ type = g_type_from_name (class_name);
+ if (!g_type_is_a (type, GTK_TYPE_WIDGET))
+ continue;
+
+ object = elem;
+ }
+
+ if (object)
+ {
+ g_free (child->element_name);
+ g_strfreev (child->attribute_names);
+ g_strfreev (child->attribute_values);
+ child->element_name = g_strdup ("property");
+ child->attribute_names = g_new0 (char *, 2);
+ child->attribute_names[0] = g_strdup ("name");
+ child->attribute_values = g_new0 (char *, 2);
+ child->attribute_values[0] = g_strdup ("child");
+ break;
+ }
+ }
+}
+
+static gboolean
+remove_boolean_prop (Element *element,
+ MyParserData *data,
+ const char *prop_name,
+ gboolean *value)
+{
+ GList *l;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "property") &&
+ has_attribute (child, "name", prop_name))
+ {
+ *value = strcmp (canonical_boolean_value (data, child->data), "1") == 0;
+ element->children = g_list_remove (element->children, child);
+ free_element (child);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+rewrite_radio_button (Element *element,
+ MyParserData *data)
+{
+ gboolean draw_indicator = TRUE;
+
+ if (!remove_boolean_prop (element, data, "draw-indicator", &draw_indicator))
+ remove_boolean_prop (element, data, "draw_indicator", &draw_indicator);
+
+ if (draw_indicator)
+ set_attribute_value (element, "class", "GtkCheckButton");
+ else
+ set_attribute_value (element, "class", "GtkToggleButton");
+
+}
+
+static gboolean
+has_prop (Element *element,
+ MyParserData *data,
+ const char *prop_name)
+{
+ GList *l;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "property") &&
+ has_attribute (child, "name", prop_name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+rewrite_scale (Element *element,
+ MyParserData *data)
+{
+ if (!has_prop (element, data, "draw-value") &&
+ !has_prop (element, data, "draw_value"))
+ {
+ Element *child;
+ child = add_element (element, "property");
+ set_attribute_value (child, "name", "draw-value");
+ child->data = g_strdup ("1");
+ }
+}
+
+static void
+rewrite_requires (Element *element,
+ MyParserData *data)
+{
+ if (has_attribute (element, "lib", "gtk+"))
+ {
+ set_attribute_value (element, "lib", "gtk");
+ set_attribute_value (element, "version", "4.0");
+ }
+}
+
+static void
+rewrite_overlay (Element *element,
+ MyParserData *data)
+{
+ GList *l, *ll;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "child"))
+ {
+ Element *object = NULL;
+ Element *packing = NULL;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "object"))
+ object = elt2;
+
+ if (g_str_equal (elt2->element_name, "packing"))
+ packing = elt2;
+ }
+
+ if (object && packing)
+ {
+ child->children = g_list_remove (child->children, packing);
+
+ for (ll = packing->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "property") &&
+ (has_attribute (elt2, "name", "pass-through") ||
+ has_attribute (elt2, "name", "pass_through")))
+ {
+ const char *b = canonical_boolean_value (data, elt2->data);
+ if (g_str_equal (b, "1"))
+ {
+ Element *new_prop;
+
+ new_prop = add_element (object, "property");
+ set_attribute_value (new_prop, "name", "can-target");
+ new_prop->data = g_strdup ("0");
+ }
+ break;
+ }
+ }
+
+ free_element (packing);
+ }
+ }
+ }
+}
+
+static void
+rewrite_toolbar (Element *element,
+ MyParserData *data)
+{
+ GList *l, *ll;
+ Element *style = NULL;
+
+ set_attribute_value (element, "class", "GtkBox");
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "property") &&
+ (has_attribute (child, "name", "toolbar_style") ||
+ has_attribute (child, "name", "toolbar-style")))
+ {
+ element->children = g_list_remove (element->children, child);
+ free_element (child);
+ break;
+ }
+ }
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ Element *object = NULL;
+ Element *packing = NULL;
+
+ if (g_str_equal (child->element_name, "style"))
+ style = child;
+
+ if (!g_str_equal (child->element_name, "child"))
+ continue;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "object"))
+ object = elt2;
+
+ if (g_str_equal (elt2->element_name, "packing"))
+ packing = elt2;
+ }
+
+ if (object)
+ {
+ const char *class_name;
+
+ class_name = get_class_name (object);
+
+ if (g_str_equal (class_name, "GtkToolButton"))
+ {
+ set_attribute_value (object, "class", "GtkButton");
+ }
+ else if (g_str_equal (class_name, "GtkToggleToolButton") ||
+ g_str_equal (class_name, "GtkRadioToolButton"))
+ {
+ set_attribute_value (object, "class", "GtkToggleButton");
+ }
+ else if (g_str_equal (class_name, "GtkSeparatorToolItem"))
+ {
+ Element *prop;
+
+ set_attribute_value (object, "class", "GtkSeparator");
+ prop = add_element (object, "property");
+ set_attribute_value (prop, "name", "orientation");
+ prop->data = g_strdup ("vertical");
+ }
+ }
+
+ if (packing)
+ child->children = g_list_remove (child->children, packing);
+ }
+
+ if (!style)
+ style = add_element (element, "style");
+
+ set_attribute_value (add_element (style, "class"), "name", "toolbar");
+}
+
+static void
+rewrite_fixed (Element *element,
+ MyParserData *data)
+{
+ GList *l, *ll;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+
+ if (g_str_equal (child->element_name, "child"))
+ {
+ Element *object = NULL;
+ Element *packing = NULL;
+
+ for (ll = child->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+
+ if (g_str_equal (elt2->element_name, "object"))
+ object = elt2;
+
+ if (g_str_equal (elt2->element_name, "packing"))
+ packing = elt2;
+ }
+
+ if (object && packing)
+ {
+ int x = 0;
+ int y = 0;
+ Element *layout;
+ Element *new_prop;
+ GskTransform *transform;
+
+ for (ll = packing->children; ll; ll = ll->next)
+ {
+ Element *elt2 = ll->data;
+ GValue value = G_VALUE_INIT;
+
+ if (has_attribute (elt2, "name", "x"))
+ {
+ if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
+ x = g_value_get_int (&value);
+ }
+ else if (has_attribute (elt2, "name", "y"))
+ {
+ if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
+ y = g_value_get_int (&value);
+ }
+ }
+
+ child->children = g_list_remove (child->children, packing);
+ free_element (packing);
+
+ layout = add_element (object, "layout");
+ new_prop = add_element (layout, "property");
+ set_attribute_value (new_prop, "name", "transform");
+
+ transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y));
+ new_prop->data = gsk_transform_to_string (transform);
+ gsk_transform_unref (transform);
+ }
+ }
+ }
+}
+
+/* returns TRUE to remove the element from the parent */
+static gboolean
+simplify_element (Element *element,
+ MyParserData *data)
+{
+ GList *l;
+
+ if (!is_pcdata_element (element))
+ g_clear_pointer (&element->data, g_free);
+ else if (g_str_equal (element->element_name, "property") &&
+ property_is_boolean (element, data))
+ {
+ const char *b = canonical_boolean_value (data, element->data);
+ g_free (element->data);
+ element->data = g_strdup (b);
+ }
+
+ l = element->children;
+ while (l)
+ {
+ GList *next = l->next;
+ Element *child = l->data;
+ if (simplify_element (child, data))
+ {
+ element->children = g_list_remove (element->children, child);
+ free_element (child);
+ }
+ l = next;
+ }
+
+ if (is_container_element (element) && element->children == NULL)
+ return TRUE;
+
+ if (g_str_equal (element->element_name, "property") &&
+ property_can_be_omitted (element, data))
+ return TRUE;
+
+ if (g_str_equal (element->element_name, "binding"))
+ {
+ const char *property_name = get_attribute_value (element, "name");
+ const char *class_name = get_class_name (element);
+ if (!get_property_pspec (data, class_name, property_name, PROP_KIND_OBJECT))
+ warn_missing_property (element, data, class_name, property_name, PROP_KIND_OBJECT);
+ }
+
+ return FALSE;
+}
+
+static void
+simplify_tree (MyParserData *data)
+{
+ simplify_element (data->root, data);
+}
+
+static gboolean
+rewrite_element (Element *element,
+ MyParserData *data)
+{
+ GList *l;
+
+ l = element->children;
+ while (l)
+ {
+ GList *next = l->next;
+ Element *child = l->data;
+ if (rewrite_element (child, data))
+ {
+ element->children = g_list_remove (element->children, child);
+ free_element (child);
+ }
+ l = next;
+ }
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkStack"))
+ rewrite_stack (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkAssistant"))
+ rewrite_assistant (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkNotebook"))
+ rewrite_notebook (element, data);
+
+ if (element_is_object_or_template (element) &&
+ (g_str_equal (get_class_name (element), "GtkActionBar") ||
+ g_str_equal (get_class_name (element), "GtkHeaderBar")))
+ rewrite_pack_type (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkToolbar"))
+ rewrite_toolbar (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkPaned"))
+ rewrite_paned (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkDialog"))
+ rewrite_dialog (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkOverlay"))
+ rewrite_overlay (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkGrid"))
+ rewrite_grid_layout (element, data);
+
+ if (element_is_object_or_template (element) &&
+ (g_str_equal (get_class_name (element), "GtkHBox") ||
+ g_str_equal (get_class_name (element), "GtkVBox") ||
+ g_str_equal (get_class_name (element), "GtkBox")))
+ rewrite_box (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkFixed"))
+ rewrite_fixed (element, data);
+
+ if (element_is_object_or_template (element) &&
+ (g_str_equal (get_class_name (element), "GtkAspectFrame") ||
+ g_str_equal (get_class_name (element), "GtkComboBox") ||
+ g_str_equal (get_class_name (element), "GtkComboBoxText") ||
+ g_str_equal (get_class_name (element), "GtkFlowBoxChild") ||
+ g_str_equal (get_class_name (element), "GtkFrame") ||
+ g_str_equal (get_class_name (element), "GtkListBoxRow") ||
+ g_str_equal (get_class_name (element), "GtkOverlay") ||
+ g_str_equal (get_class_name (element), "GtkPopover") ||
+ g_str_equal (get_class_name (element), "GtkPopoverMenu") ||
+ g_str_equal (get_class_name (element), "GtkRevealer") ||
+ g_str_equal (get_class_name (element), "GtkScrolledWindow") ||
+ g_str_equal (get_class_name (element), "GtkSearchBar") ||
+ g_str_equal (get_class_name (element), "GtkViewport") ||
+ g_str_equal (get_class_name (element), "GtkWindow")))
+ rewrite_bin_child (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkRadioButton"))
+ rewrite_radio_button (element, data);
+
+ if (element_is_object_or_template (element) &&
+ g_str_equal (get_class_name (element), "GtkScale"))
+ rewrite_scale (element, data);
+
+ if (g_str_equal (element->element_name, "property"))
+ maybe_rename_property (element, data);
+
+ if (g_str_equal (element->element_name, "property") &&
+ property_has_been_removed (element, data))
+ return TRUE;
+
+ if (g_str_equal (element->element_name, "requires"))
+ rewrite_requires (element, data);
+
+ return FALSE;
+}
+
+static void
+rewrite_tree (MyParserData *data)
+{
+ rewrite_element (data->root, data);
+}
+
+/* For properties which have changed their default
+ * value between 3 and 4, we make sure that their
+ * old default value is present in the tree before
+ * simplifying it.
+ *
+ * So far, this is just GtkWidget::visible,
+ * changing its default from 0 to 1.
+ */
+static void
+add_old_default_properties (Element *element,
+ MyParserData *data)
+{
+ const char *class_name;
+ GType type;
+
+ if (!g_str_equal (element->element_name, "object"))
+ return;
+
+ class_name = get_class_name (element);
+ type = g_type_from_name (class_name);
+ if (g_type_is_a (type, GTK_TYPE_WIDGET))
+ {
+ GList *l;
+ gboolean has_visible = FALSE;
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *prop = l->data;
+ const char *name = get_attribute_value (prop, "name");
+
+ if (g_str_equal (prop->element_name, "property") &&
+ g_str_equal (name, "visible"))
+ has_visible = TRUE;
+ }
+
+ if (!has_visible)
+ {
+ Element *new_prop;
+
+ new_prop = add_element (element, "property");
+ set_attribute_value (new_prop, "name", "visible");
+ new_prop->data = g_strdup ("0");
+ }
+ }
+}
+
+static void
+enhance_element (Element *element,
+ MyParserData *data)
+{
+ GList *l;
+
+ if (strcmp (element->element_name, "requires") == 0 &&
+ has_attribute (element, "lib", "gtk+"))
+ {
+ data->has_gtk_requires = TRUE;
+ }
+
+ add_old_default_properties (element, data);
+
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ enhance_element (child, data);
+ }
+
+ if (element == data->root && !data->has_gtk_requires)
+ {
+ Element *requires = add_element (element, "requires");
+ set_attribute_value (requires, "lib", "gtk+");
+ set_attribute_value (requires, "version", "3.0");
+ }
+}
+
+static void
+enhance_tree (MyParserData *data)
+{
+ enhance_element (data->root, data);
+}
+
+static void
+dump_element (Element *element,
+ FILE *output,
+ int indent)
+{
+ g_fprintf (output, "%*s<%s", indent, "", element->element_name);
+ if (element->attribute_names[0])
+ {
+ int i;
+ for (i = 0; element->attribute_names[i]; i++)
+ {
+ char *escaped = g_markup_escape_text (element->attribute_values[i], -1);
+ g_fprintf (output, " %s=\"%s\"", element->attribute_names[i], escaped);
+ g_free (escaped);
+ }
+ }
+ if (element->children || element->data)
+ {
+ g_fprintf (output, ">");
+
+ if (element->children)
+ {
+ GList *l;
+
+ g_fprintf (output, "\n");
+ for (l = element->children; l; l = l->next)
+ {
+ Element *child = l->data;
+ dump_element (child, output, indent + 2);
+ }
+ g_fprintf (output, "%*s", indent, "");
+ }
+ else
+ {
+ if (is_cdata_property (element))
+ {
+ g_fprintf (output, "<![CDATA[");
+ g_fprintf (output, "%s", element->data);
+ g_fprintf (output, "]]>");
+ }
+ else
+ {
+ char *escaped = g_markup_escape_text (element->data, -1);
+ g_fprintf (output, "%s", escaped);
+ g_free (escaped);
+ }
+ }
+ g_fprintf (output, "</%s>\n", element->element_name);
+ }
+ else
+ g_fprintf (output, "/>\n");
+}
+
+static void
+write_xml_declaration (FILE *output)
+{
+ g_fprintf (output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+}
+
+static void
+dump_tree (MyParserData *data)
+{
+ write_xml_declaration (data->output);
+ dump_element (data->root, data->output, 0);
+}
+
+static gboolean
+simplify_file (const char *filename,
+ gboolean replace,
+ gboolean convert3to4)
+{
+ GMarkupParseContext *context;
+ char *buffer;
+ MyParserData data;
+ GError *error = NULL;
+
+ data.input_filename = filename;
+ data.output_filename = NULL;
+ data.convert3to4 = convert3to4;
+ data.has_gtk_requires = FALSE;
+
+ if (replace)
+ {
+ int fd;
+ fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
+ data.output = fdopen (fd, "w");
+ }
+ else
+ {
+ data.output = stdout;
+ }
+
+ if (!g_file_get_contents (filename, &buffer, NULL, &error))
+ {
+ g_printerr (_("Can’t load “%s”: %s\n"), filename, error->message);
+ return FALSE;
+ }
+
+ data.root = NULL;
+ data.current = NULL;
+ data.value = g_string_new ("");
+
+ context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
+ if (!g_markup_parse_context_parse (context, buffer, -1, &error))
+ {
+ g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
+ return FALSE;
+ }
+
+ data.builder = gtk_builder_new ();
+
+ if (data.convert3to4)
+ {
+ enhance_tree (&data);
+ rewrite_tree (&data);
+ }
+ simplify_tree (&data);
+
+ dump_tree (&data);
+
+ fclose (data.output);
+
+ if (data.output_filename)
+ {
+ char *content;
+ gsize length;
+
+ if (!g_file_get_contents (data.output_filename, &content, &length, &error))
+ {
+ g_printerr (_("Failed to read “%s”: %s\n"), data.output_filename, error->message);
+ return FALSE;
+ }
+
+ if (!g_file_set_contents (data.input_filename, content, length, &error))
+ {
+ g_printerr (_("Failed to write %s: “%s”\n"), data.input_filename, error->message);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void
+do_simplify (int *argc,
+ const char ***argv)
+{
+ gboolean replace = FALSE;
+ gboolean convert3to4 = FALSE;
+ char **filenames = NULL;
+ GOptionContext *ctx;
+ const GOptionEntry entries[] = {
+ { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
+ { "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+ { NULL, }
+ };
+ GError *error = NULL;
+ int i;
+
+ ctx = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (ctx, FALSE);
+ g_option_context_add_main_entries (ctx, entries, NULL);
+
+ if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ g_option_context_free (ctx);
+
+ if (filenames == NULL)
+ {
+ g_printerr (_("No .ui file specified\n"));
+ exit (1);
+ }
+
+ if (g_strv_length (filenames) > 1 && !replace)
+ {
+ g_printerr (_("Can only simplify a single .ui file without --replace\n"));
+ exit (1);
+ }
+
+ for (i = 0; filenames[i]; i++)
+ {
+ if (!simplify_file (filenames[i], replace, convert3to4))
+ exit (1);
+ }
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static GType
+make_fake_type (const char *type_name,
+ const char *parent_name)
+{
+ GType parent_type;
+ GTypeQuery query;
+
+ parent_type = g_type_from_name (parent_name);
+ if (parent_type == G_TYPE_INVALID)
+ {
+ g_printerr ("Failed to lookup template parent type %s\n", parent_name);
+ exit (1);
+ }
+
+ g_type_query (parent_type, &query);
+ return g_type_register_static_simple (parent_type,
+ type_name,
+ query.class_size,
+ NULL,
+ query.instance_size,
+ NULL,
+ 0);
+}
+
+static void
+do_validate_template (const char *filename,
+ const char *type_name,
+ const char *parent_name)
+{
+ GType template_type;
+ GObject *object;
+ GtkBuilder *builder;
+ GError *error = NULL;
+ int ret;
+
+ /* Only make a fake type if it doesn't exist yet.
+ * This lets us e.g. validate the GtkFileChooserWidget template.
+ */
+ template_type = g_type_from_name (type_name);
+ if (template_type == G_TYPE_INVALID)
+ template_type = make_fake_type (type_name, parent_name);
+
+ object = g_object_new (template_type, NULL);
+ if (!object)
+ {
+ g_printerr ("Failed to create an instance of the template type %s\n", type_name);
+ exit (1);
+ }
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
+ if (ret)
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+ g_object_unref (builder);
+
+ if (ret == 0)
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+}
+
+static gboolean
+parse_template_error (const char *message,
+ char **class_name,
+ char **parent_name)
+{
+ char *p;
+
+ p = strstr (message, "(class '");
+ if (p)
+ {
+ *class_name = g_strdup (p + strlen ("(class '"));
+ p = strstr (*class_name, "'");
+ if (p)
+ *p = '\0';
+ }
+ p = strstr (message, ", parent '");
+ if (p)
+ {
+ *parent_name = g_strdup (p + strlen (", parent '"));
+ p = strstr (*parent_name, "'");
+ if (p)
+ *p = '\0';
+ }
+
+ return TRUE;
+}
+
+static gboolean
+validate_file (const char *filename)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ int ret;
+ char *class_name = NULL;
+ char *parent_name = NULL;
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+ g_object_unref (builder);
+
+ if (ret == 0)
+ {
+ if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
+ parse_template_error (error->message, &class_name, &parent_name))
+ {
+ do_validate_template (filename, class_name, parent_name);
+ }
+ else
+ {
+ g_printerr ("%s\n", error->message);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void
+do_validate (int *argc, const char ***argv)
+{
+ int i;
+
+ for (i = 1; i < *argc; i++)
+ {
+ if (!validate_file ((*argv)[i]))
+ exit (1);
+ }
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static void G_GNUC_NORETURN
+usage (void)
+{
+ g_print (_("Usage:\n"
+ " gtk-builder-tool [COMMAND] [OPTION…] FILE\n"
+ "\n"
+ "Commands:\n"
+ " validate Validate the file\n"
+ " simplify Simplify the file\n"
+ " enumerate List all named objects\n"
+ " preview Preview the file\n"
+ "\n"
+ "Simplify Options:\n"
+ " --replace Replace the file\n"
+ " --3to4 Convert from GTK 3 to GTK 4\n"
+ "\n"
+ "Preview Options:\n"
+ " --id=ID Preview only the named object\n"
+ " --css=FILE Use style from CSS file\n"
+ "\n"
+ "Perform various tasks on GtkBuilder .ui files.\n"));
+ exit (1);
+}
+
+/* A simplified version of g_log_writer_default_would_drop(), to avoid
+ * bumping up the required version of GLib to 2.68
+ */
+static gboolean
+would_drop (GLogLevelFlags level,
+ const char *domain)
+{
+ return (level & (G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_WARNING)) == 0;
+}
+
+static GLogWriterOutput
+log_writer_func (GLogLevelFlags level,
+ const GLogField *fields,
+ gsize n_fields,
+ gpointer user_data)
+{
+ gsize i;
+ const char *domain = NULL;
+ const char *message = NULL;
+
+ for (i = 0; i < n_fields; i++)
+ {
+ if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
+ domain = fields[i].value;
+ else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
+ message = fields[i].value;
+ }
+
+ if (message != NULL && !would_drop (level, domain))
+ {
+ const char *prefix;
+ switch (level & G_LOG_LEVEL_MASK)
+ {
+ case G_LOG_LEVEL_ERROR:
+ prefix = "ERROR";
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ prefix = "CRITICAL";
+ break;
+ case G_LOG_LEVEL_WARNING:
+ prefix = "WARNING";
+ break;
+ default:
+ prefix = "INFO";
+ break;
+ }
+ g_printerr ("%s-%s: %s\n", domain, prefix, message);
+ }
+
+ return G_LOG_WRITER_HANDLED;
+}
+
+int
+main (int argc, const char *argv[])
+{
+ g_set_prgname ("gtk-builder-tool");
+
+ g_log_set_writer_func (log_writer_func, NULL, NULL);
+
+ gtk_init ();
+
+ gtk_test_register_all_types ();
+
+ if (argc < 3)
+ usage ();
+
+ if (strcmp (argv[2], "--help") == 0)
+ usage ();
+
+ argv++;
+ argc--;
+
+ if (strcmp (argv[0], "validate") == 0)
+ do_validate (&argc, &argv);
+ else if (strcmp (argv[0], "simplify") == 0)
+ do_simplify (&argc, &argv);
+ else if (strcmp (argv[0], "enumerate") == 0)
+ do_enumerate (&argc, &argv);
+ else if (strcmp (argv[0], "preview") == 0)
+ do_preview (&argc, &argv);
+ else
+ usage ();
+
+ return 0;
+}
--- /dev/null
+
+#ifndef __GTK_BUILDER_TOOL_H__
+#define __GTK_BUILDER_TOOL_H__
+
+void do_simplify (int *argc, const char ***argv);
+void do_validate (int *argc, const char ***argv);
+void do_enumerate (int *argc, const char ***argv);
+void do_preview (int *argc, const char ***argv);
+
+#endif
--- /dev/null
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+#include <gio/gdesktopappinfo.h>
+#endif
+#include <gtk.h>
+
+static gboolean show_version;
+static char **args = NULL;
+
+static GOptionEntry entries[] = {
+ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
+ { NULL}
+};
+
+int
+main (int argc, char *argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context = NULL;
+ char *summary;
+ char *app_name;
+#ifdef G_OS_UNIX
+ char *desktop_file_name;
+ char *bus_name = NULL;
+#endif
+ GAppInfo *info = NULL;
+ GAppLaunchContext *launch_context;
+ GList *l;
+ GFile *f;
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ context =
+ /* Translators: this message will appear immediately after the */
+ /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE> */
+ g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
+
+ /* Translators: this message will appear after the usage string */
+ /* and before the list of options. */
+ summary = _("Launch an application (specified by its desktop file name),\n"
+ "optionally passing one or more URIs as arguments.");
+ g_option_context_set_summary (context, summary);
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_parse (context, &argc, &argv, &error);
+
+ g_option_context_free (context);
+
+ if (error != NULL)
+ {
+ g_printerr (_("Error parsing commandline options: %s\n"), error->message);
+ g_printerr ("\n");
+ g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+ g_printerr ("\n");
+ g_error_free (error);
+ return 1;
+ }
+
+ if (show_version)
+ {
+ g_print ("%d.%d.%d\n",
+ gtk_get_major_version (),
+ gtk_get_minor_version (),
+ gtk_get_micro_version ());
+ return 0;
+ }
+
+ if (!args)
+ {
+ /* Translators: the %s is the program name. This error message */
+ /* means the user is calling gtk-launch without any argument. */
+ g_printerr (_("%s: missing application name"), g_get_prgname ());
+ g_printerr ("\n");
+ g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+ g_printerr ("\n");
+ return 1;
+ }
+
+
+ gtk_init ();
+
+ app_name = *args;
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+ bus_name = g_strdup (app_name);
+ if (g_str_has_suffix (app_name, ".desktop"))
+ {
+ desktop_file_name = g_strdup (app_name);
+ bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
+ }
+ else
+ {
+ desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
+ }
+
+ if (!g_dbus_is_name (bus_name))
+ g_clear_pointer (&bus_name, g_free);
+ info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
+ g_free (desktop_file_name);
+#else
+#warning Please add support for creating AppInfo from id for your OS
+ g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
+#endif
+ args++;
+
+ if (!info)
+ {
+ /* Translators: the first %s is the program name, the second one */
+ /* is the application name. */
+ g_printerr (_("%s: no such application %s"),
+ g_get_prgname (), app_name);
+ g_printerr ("\n");
+ return 2;
+ }
+
+ l = NULL;
+ for (; *args; args++)
+ {
+ f = g_file_new_for_commandline_arg (*args);
+ l = g_list_append (l, f);
+ }
+
+ launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
+ if (!g_app_info_launch (info, l, launch_context, &error))
+ {
+ /* Translators: the first %s is the program name, the second one */
+ /* is the error message. */
+ g_printerr (_("%s: error launching application: %s\n"),
+ g_get_prgname (), error->message);
+ return 3;
+ }
+ g_object_unref (info);
+ g_object_unref (launch_context);
+
+#ifdef G_OS_UNIX
+ if (bus_name != NULL)
+ {
+ GDBusConnection *connection;
+ char *object_path, *p;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ object_path = g_strdup_printf ("/%s", bus_name);
+ for (p = object_path; *p != '\0'; p++)
+ if (*p == '.')
+ *p = '/';
+
+ if (connection)
+ g_dbus_connection_call_sync (connection,
+ bus_name,
+ object_path,
+ "org.freedesktop.DBus.Peer",
+ "Ping",
+ NULL, NULL,
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+ g_clear_pointer (&object_path, g_free);
+ g_clear_object (&connection);
+ g_clear_pointer (&bus_name, g_free);
+ }
+#endif
+ g_list_free_full (l, g_object_unref);
+
+ return 0;
+}
--- /dev/null
+/* Copyright 2015 Timm Bäder
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+
+int
+main (int argc, char **argv)
+{
+ GtkSettings *settings;
+ GParamSpec **props;
+ guint n_properties;
+ guint i;
+ int max_prop_name_length = 0;
+ char *pattern = NULL;
+
+ gtk_init ();
+
+ if (argc > 1)
+ pattern = argv[1];
+
+ settings = gtk_settings_get_default ();
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
+
+ for (i = 0; i < n_properties; i ++)
+ {
+ int len = strlen (props[i]->name);
+
+ if (len > max_prop_name_length)
+ max_prop_name_length = len;
+ }
+
+
+ for (i = 0; i < n_properties; i ++)
+ {
+ GValue value = {0};
+ GParamSpec *prop = props[i];
+ char *value_str;
+ int spacing = max_prop_name_length - strlen (prop->name) + 1;
+ gboolean deprecated;
+
+ if (pattern && !g_strrstr (prop->name, pattern))
+ continue;
+
+ g_value_init (&value, prop->value_type);
+ g_object_get_property (G_OBJECT (settings), prop->name, &value);
+ deprecated = prop->flags & G_PARAM_DEPRECATED;
+
+ if (G_VALUE_HOLDS_ENUM (&value))
+ {
+ GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
+ GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
+
+ value_str = g_strdup (enum_value->value_name);
+ }
+ else
+ {
+ value_str = g_strdup_value_contents (&value);
+ }
+
+ if (deprecated)
+ {
+ printf ("!");
+ spacing --;
+ }
+
+ for (; spacing >= 0; spacing --)
+ printf (" ");
+
+ printf ("%s: %s\n", prop->name, value_str);
+
+ g_free (value_str);
+ g_value_unset (&value);
+ }
+
+ g_free (props);
+
+ return 0;
+}
--- /dev/null
+<?xml version="1.0"?>
+<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+ xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
+ version="2.0">
+ <its:translateRule selector="/interface" translate="no"/>
+ <its:translateRule selector="/interface//*[@translatable = 'yes']"
+ translate="yes"/>
+
+ <!-- The 'comment' attribute should be extracted as a translator comment. -->
+ <its:locNoteRule selector="/interface//*[@comments]"
+ locNotePointer="@comments"
+ locNoteType="alert"/>
+ <gt:escapeRule selector="/interface//@comments" escape="no"/>
+
+ <!-- The 'context' attribute should be extracted as msgctxt. -->
+ <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
+
+ <its:preserveSpaceRule selector="/interface" space="preserve"/>
+
+ <!-- Extracted strings are consumed by the library and are never
+ merged back; we don't want to escape special characters. -->
+ <gt:escapeRule selector="/interface" escape="no"/>
+</its:rules>
--- /dev/null
+<?xml version="1.0"?>
+<locatingRules>
+ <locatingRule name="GtkBuilder" pattern="*.ui">
+ <documentRule localName="interface" target="gtkbuilder.its"/>
+ </locatingRule>
+</locatingRules>
--- /dev/null
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
+ <start>
+ <element name="interface">
+ <optional>
+ <attribute name="domain">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="requires"/>
+ <ref name="object"/>
+ <ref name="template"/>
+ <ref name="menu"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </start>
+ <define name="requires">
+ <element name="requires">
+ <attribute name="lib">
+ <text/>
+ </attribute>
+ <attribute name="version">
+ <text/>
+ </attribute>
+ </element>
+ </define>
+ <define name="object">
+ <element name="object">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <attribute name="class">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="type-func">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="constructor">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="property"/>
+ <ref name="signal"/>
+ <ref name="child"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="template">
+ <element name="template">
+ <attribute name="class">
+ <text/>
+ </attribute>
+ <attribute name="parent">
+ <text/>
+ </attribute>
+ <zeroOrMore>
+ <choice>
+ <ref name="property"/>
+ <ref name="signal"/>
+ <ref name="child"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="property">
+ <element name="property">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="translatable">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="comments">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="context">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <group>
+ <attribute name="bind-source">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="bind-property">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="bind-flags">
+ <text/>
+ </attribute>
+ </optional>
+ </group>
+ </optional>
+ <optional>
+ <text/>
+ </optional>
+ </element>
+ </define>
+ <define name="signal">
+ <element name="signal">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <attribute name="handler">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="after">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="swapped">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="object">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="last_modification_time">
+ <text/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+ <define name="child">
+ <element name="child">
+ <optional>
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="internal-child">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="object"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="menu">
+ <element name="menu">
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ <optional>
+ <attribute name="domain">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="item">
+ <element name="item">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="link"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="attribute_">
+ <element name="attribute">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="translatable">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="context">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="comments">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <text/>
+ </optional>
+ </element>
+ </define>
+ <define name="link">
+ <element name="link">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <zeroOrMore>
+ <ref name="item"/>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="submenu">
+ <element name="submenu">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="section">
+ <element name="section">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="ANY">
+ <element>
+ <anyName>
+ <except>
+ <name>interface</name>
+ <name>requires</name>
+ <name>object</name>
+ <name>property</name>
+ <name>signal</name>
+ <name>child</name>
+ <name>menu</name>
+ <name>item</name>
+ <name>attribute</name>
+ <name>link</name>
+ <name>submenu</name>
+ <name>section</name>
+ </except>
+ </anyName>
+ <zeroOrMore>
+ <attribute>
+ <anyName/>
+ <text/>
+ </attribute>
+ </zeroOrMore>
+ <interleave>
+ <zeroOrMore>
+ <ref name="ALL"/>
+ </zeroOrMore>
+ <optional>
+ <text/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="ALL">
+ <element>
+ <anyName/>
+ <zeroOrMore>
+ <attribute>
+ <anyName/>
+ <text/>
+ </attribute>
+ </zeroOrMore>
+ <interleave>
+ <zeroOrMore>
+ <ref name="ALL"/>
+ </zeroOrMore>
+ <optional>
+ <text/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+</grammar>
--- /dev/null
+# Installed tools
+gtk_tools = [
+ ['gtk4-query-settings', ['gtk-query-settings.c'], []],
+ ['gtk4-builder-tool', ['gtk-builder-tool.c',
+ 'gtk-builder-tool-simplify.c',
+ 'gtk-builder-tool-validate.c',
+ 'gtk-builder-tool-enumerate.c',
+ 'gtk-builder-tool-preview.c'], [] ],
+ ['gtk4-update-icon-cache', ['updateiconcache.c'], [ libgtk_static ] ],
+ ['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static ] ],
+]
+
+if os_unix
+ gtk_tools += [['gtk4-launch', ['gtk-launch.c'], []]]
+endif
+
+foreach tool: gtk_tools
+ tool_name = tool.get(0)
+ tool_srcs = tool.get(1)
+ tool_libs = tool.get(2)
+
+ exe = executable(tool_name,
+ sources: tool_srcs,
+ include_directories: [confinc],
+ c_args: common_cflags,
+ link_with: tool_libs,
+ dependencies: libgtk_dep,
+ install: true,
+ )
+
+ set_variable(tool_name.underscorify(), exe) # used in testsuites
+endforeach
+
+# Data to install
+install_data('gtk4builder.rng', install_dir: gtk_datadir / 'gtk-4.0')
+
+install_data([
+ 'gtk4builder.loc',
+ 'gtk4builder.its',
+ ],
+ install_dir: gtk_datadir / 'gettext/its',
+)
--- /dev/null
+/* updateiconcache.c
+ * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#ifdef _MSC_VER
+#include <io.h>
+#include <sys/utime.h>
+#else
+#include <utime.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include <glib/gi18n.h>
+#include "gtkiconcachevalidatorprivate.h"
+
+static gboolean force_update = FALSE;
+static gboolean ignore_theme_index = FALSE;
+static gboolean quiet = FALSE;
+static gboolean index_only = TRUE;
+static gboolean validate = FALSE;
+static char *var_name = (char *) "-";
+
+#define CACHE_NAME "icon-theme.cache"
+
+#define HAS_SUFFIX_XPM (1 << 0)
+#define HAS_SUFFIX_SVG (1 << 1)
+#define HAS_SUFFIX_PNG (1 << 2)
+#define HAS_ICON_FILE (1 << 3)
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+#define HASH_OFFSET 12
+
+#define ALIGN_VALUE(this, boundary) \
+ (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+#ifdef HAVE_FTW_H
+
+#include <ftw.h>
+
+static GStatBuf cache_dir_stat;
+static gboolean cache_up_to_date;
+
+static int check_dir_mtime (const char *dir,
+ const struct stat *sb,
+ int tf)
+{
+ if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
+ {
+ cache_up_to_date = FALSE;
+ /* stop tree walk */
+ return 1;
+ }
+
+ return 0;
+}
+
+static gboolean
+is_cache_up_to_date (const char *path)
+{
+ char *cache_path;
+ int retval;
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ retval = g_stat (cache_path, &cache_dir_stat);
+ g_free (cache_path);
+
+ if (retval < 0)
+ {
+ /* Cache file not found */
+ return FALSE;
+ }
+
+ cache_up_to_date = TRUE;
+
+ ftw (path, check_dir_mtime, 20);
+
+ return cache_up_to_date;
+}
+
+#else /* !HAVE_FTW_H */
+
+gboolean
+is_cache_up_to_date (const char *path)
+{
+ GStatBuf path_stat, cache_stat;
+ char *cache_path;
+ int retval;
+
+ retval = g_stat (path, &path_stat);
+
+ if (retval < 0)
+ {
+ /* We can't stat the path,
+ * assume we have a updated cache */
+ return TRUE;
+ }
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ retval = g_stat (cache_path, &cache_stat);
+ g_free (cache_path);
+
+ if (retval < 0)
+ {
+ /* Cache file not found */
+ return FALSE;
+ }
+
+ /* Check mtime */
+ return cache_stat.st_mtime >= path_stat.st_mtime;
+}
+
+#endif /* !HAVE_FTW_H */
+
+static gboolean
+has_theme_index (const char *path)
+{
+ gboolean result;
+ char *index_path;
+
+ index_path = g_build_filename (path, "index.theme", NULL);
+
+ result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
+
+ g_free (index_path);
+
+ return result;
+}
+
+
+typedef struct
+{
+ GdkPixdata pixdata;
+ gboolean has_pixdata;
+ guint32 offset;
+ guint size;
+} ImageData;
+
+typedef struct
+{
+ int has_embedded_rect;
+ int x0, y0, x1, y1;
+
+ int n_attach_points;
+ int *attach_points;
+
+ int n_display_names;
+ char **display_names;
+
+ guint32 offset;
+ int size;
+} IconData;
+
+static GHashTable *image_data_hash = NULL;
+static GHashTable *icon_data_hash = NULL;
+
+typedef struct
+{
+ int flags;
+ int dir_index;
+
+ ImageData *image_data;
+ guint pixel_data_size;
+
+ IconData *icon_data;
+ guint icon_data_size;
+} Image;
+
+
+static gboolean
+foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
+{
+ Image *image = (Image *)value;
+ GHashTable *files = user_data;
+ GList *list;
+ gboolean free_key = FALSE;
+
+ if (image->flags == HAS_ICON_FILE)
+ {
+ /* just a .icon file, throw away */
+ g_free (key);
+ g_free (image);
+
+ return TRUE;
+ }
+
+ list = g_hash_table_lookup (files, key);
+ if (list)
+ free_key = TRUE;
+
+ list = g_list_prepend (list, value);
+ g_hash_table_insert (files, key, list);
+
+ if (free_key)
+ g_free (key);
+
+ return TRUE;
+}
+
+static IconData *
+load_icon_data (const char *path)
+{
+ GKeyFile *icon_file;
+ char **split;
+ gsize length;
+ char *str;
+ char *split_point;
+ int i;
+ int *ivalues;
+ GError *error = NULL;
+ char **keys;
+ gsize n_keys;
+ IconData *data;
+
+ icon_file = g_key_file_new ();
+ g_key_file_set_list_separator (icon_file, ',');
+ g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+ if (error)
+ {
+ g_error_free (error);
+ g_key_file_free (icon_file);
+
+ return NULL;
+ }
+
+ data = g_new0 (IconData, 1);
+
+ ivalues = g_key_file_get_integer_list (icon_file,
+ "Icon Data", "EmbeddedTextRectangle",
+ &length, NULL);
+ if (ivalues)
+ {
+ if (length == 4)
+ {
+ data->has_embedded_rect = TRUE;
+ data->x0 = ivalues[0];
+ data->y0 = ivalues[1];
+ data->x1 = ivalues[2];
+ data->y1 = ivalues[3];
+ }
+
+ g_free (ivalues);
+ }
+
+ str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
+ if (str)
+ {
+ split = g_strsplit (str, "|", -1);
+
+ data->n_attach_points = g_strv_length (split);
+ data->attach_points = g_new (int, 2 * data->n_attach_points);
+
+ for (i = 0; i < data->n_attach_points; ++i)
+ {
+ split_point = strchr (split[i], ',');
+ if (split_point)
+ {
+ *split_point = 0;
+ split_point++;
+ data->attach_points[2 * i] = atoi (split[i]);
+ data->attach_points[2 * i + 1] = atoi (split_point);
+ }
+ }
+
+ g_strfreev (split);
+ g_free (str);
+ }
+
+ keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
+ data->display_names = g_new0 (char *, 2 * n_keys + 1);
+ data->n_display_names = 0;
+
+ for (i = 0; i < n_keys; i++)
+ {
+ char *lang, *name;
+
+ if (g_str_has_prefix (keys[i], "DisplayName"))
+ {
+ char *open, *close = NULL;
+
+ open = strchr (keys[i], '[');
+
+ if (open)
+ close = strchr (open, ']');
+
+ if (open && close)
+ {
+ lang = g_strndup (open + 1, close - open - 1);
+ name = g_key_file_get_locale_string (icon_file,
+ "Icon Data", "DisplayName",
+ lang, NULL);
+ }
+ else
+ {
+ lang = g_strdup ("C");
+ name = g_key_file_get_string (icon_file,
+ "Icon Data", "DisplayName",
+ NULL);
+ }
+
+ data->display_names[2 * data->n_display_names] = lang;
+ data->display_names[2 * data->n_display_names + 1] = name;
+ data->n_display_names++;
+ }
+ }
+
+ g_strfreev (keys);
+
+ g_key_file_free (icon_file);
+
+ /* -1 means not computed yet, the real value depends
+ * on string pool state, and will be computed
+ * later
+ */
+ data->size = -1;
+
+ return data;
+}
+
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
+static void
+canonicalize_filename (char *filename)
+{
+ char *p, *q;
+ gboolean last_was_slash = FALSE;
+
+ p = filename;
+ q = filename;
+
+ while (*p)
+ {
+ if (*p == G_DIR_SEPARATOR)
+ {
+ if (!last_was_slash)
+ *q++ = G_DIR_SEPARATOR;
+
+ last_was_slash = TRUE;
+ }
+ else
+ {
+ if (last_was_slash && *p == '.')
+ {
+ if (*(p + 1) == G_DIR_SEPARATOR ||
+ *(p + 1) == '\0')
+ {
+ if (*(p + 1) == '\0')
+ break;
+
+ p += 1;
+ }
+ else if (*(p + 1) == '.' &&
+ (*(p + 2) == G_DIR_SEPARATOR ||
+ *(p + 2) == '\0'))
+ {
+ if (q > filename + 1)
+ {
+ q--;
+ while (q > filename + 1 &&
+ *(q - 1) != G_DIR_SEPARATOR)
+ q--;
+ }
+
+ if (*(p + 2) == '\0')
+ break;
+
+ p += 2;
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+
+ p++;
+ }
+
+ if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+ q--;
+
+ *q = '\0';
+}
+
+static char *
+follow_links (const char *path)
+{
+ char *target;
+ char *d, *s;
+ char *path2 = NULL;
+
+ path2 = g_strdup (path);
+ while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+ {
+ target = g_file_read_link (path2, NULL);
+
+ if (target)
+ {
+ if (g_path_is_absolute (target))
+ path2 = target;
+ else
+ {
+ d = g_path_get_dirname (path2);
+ s = g_build_filename (d, target, NULL);
+ g_free (d);
+ g_free (target);
+ g_free (path2);
+ path2 = s;
+ }
+ }
+ else
+ break;
+ }
+
+ if (strcmp (path, path2) == 0)
+ {
+ g_free (path2);
+ path2 = NULL;
+ }
+
+ return path2;
+}
+
+static void
+maybe_cache_image_data (Image *image,
+ const char *path)
+{
+ if (!index_only && !image->image_data &&
+ (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
+ {
+ GdkPixbuf *pixbuf;
+ ImageData *idata;
+ char *path2;
+
+ idata = g_hash_table_lookup (image_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ ImageData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = g_new0 (ImageData, 1);
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+ }
+
+ if (!idata->has_pixdata)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+
+ if (pixbuf)
+ {
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+ idata->size = idata->pixdata.length + 8;
+ idata->has_pixdata = TRUE;
+ }
+ }
+
+ image->image_data = idata;
+
+ g_free (path2);
+ }
+}
+
+static void
+maybe_cache_icon_data (Image *image,
+ const char *path)
+{
+ if (g_str_has_suffix (path, ".icon"))
+ {
+ IconData *idata = NULL;
+ char *path2 = NULL;
+
+ idata = g_hash_table_lookup (icon_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ IconData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (icon_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = load_icon_data (path);
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+ }
+
+ image->icon_data = idata;
+
+ g_free (path2);
+ }
+}
+
+/*
+ * Finds all dir separators and replaces them with “/”.
+ * This makes sure that only /-separated paths are written in cache files,
+ * maintaining compatibility with theme index files that use slashes as
+ * directory separators on all platforms.
+ */
+static void
+replace_backslashes_with_slashes (char *path)
+{
+ size_t i;
+ if (path == NULL)
+ return;
+ for (i = 0; path[i]; i++)
+ if (G_IS_DIR_SEPARATOR (path[i]))
+ path[i] = '/';
+}
+
+static GList *
+scan_directory (const char *base_path,
+ const char *subdir,
+ GHashTable *files,
+ GList *directories,
+ int depth)
+{
+ GHashTable *dir_hash;
+ GDir *dir;
+ GList *list = NULL, *iterator = NULL;
+ const char *name;
+ char *dir_path;
+ gboolean dir_added = FALSE;
+ guint dir_index = 0xffff;
+
+ dir_path = g_build_path ("/", base_path, subdir, NULL);
+
+ /* FIXME: Use the gerror */
+ dir = g_dir_open (dir_path, 0, NULL);
+
+ if (!dir)
+ return directories;
+
+ dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ list = g_list_prepend (list, g_strdup (name));
+ }
+ list = g_list_sort (list, (GCompareFunc) strcmp);
+ for (iterator = list; iterator; iterator = iterator->next)
+ {
+ name = iterator->data;
+
+ char *path;
+ gboolean retval;
+ int flags = 0;
+ Image *image;
+ char *basename, *dot;
+
+ path = g_build_filename (dir_path, name, NULL);
+
+ retval = g_file_test (path, G_FILE_TEST_IS_DIR);
+ if (retval)
+ {
+ char *subsubdir;
+
+ if (subdir)
+ subsubdir = g_build_path ("/", subdir, name, NULL);
+ else
+ subsubdir = g_strdup (name);
+ directories = scan_directory (base_path, subsubdir, files,
+ directories, depth + 1);
+ g_free (subsubdir);
+
+ continue;
+ }
+
+ /* ignore images in the toplevel directory */
+ if (subdir == NULL)
+ continue;
+
+ retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
+ if (retval)
+ {
+ if (g_str_has_suffix (name, ".png"))
+ flags |= HAS_SUFFIX_PNG;
+ else if (g_str_has_suffix (name, ".svg"))
+ flags |= HAS_SUFFIX_SVG;
+ else if (g_str_has_suffix (name, ".xpm"))
+ flags |= HAS_SUFFIX_XPM;
+ else if (g_str_has_suffix (name, ".icon"))
+ flags |= HAS_ICON_FILE;
+
+ if (flags == 0)
+ continue;
+
+ basename = g_strdup (name);
+ dot = strrchr (basename, '.');
+ *dot = '\0';
+
+ image = g_hash_table_lookup (dir_hash, basename);
+ if (!image)
+ {
+ if (!dir_added)
+ {
+ dir_added = TRUE;
+ if (subdir)
+ {
+ dir_index = g_list_length (directories);
+ directories = g_list_append (directories, g_strdup (subdir));
+ }
+ else
+ dir_index = 0xffff;
+ }
+
+ image = g_new0 (Image, 1);
+ image->dir_index = dir_index;
+ g_hash_table_insert (dir_hash, g_strdup (basename), image);
+ }
+
+ image->flags |= flags;
+
+ maybe_cache_image_data (image, path);
+ maybe_cache_icon_data (image, path);
+
+ g_free (basename);
+ }
+
+ g_free (path);
+ }
+
+ g_list_free_full (list, g_free);
+ g_dir_close (dir);
+
+ /* Move dir into the big file hash */
+ g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
+
+ g_hash_table_destroy (dir_hash);
+
+ return directories;
+}
+
+typedef struct _HashNode HashNode;
+
+struct _HashNode
+{
+ HashNode *next;
+ char *name;
+ GList *image_list;
+ int offset;
+};
+
+static guint
+icon_name_hash (gconstpointer key)
+{
+ const signed char *p = key;
+ guint32 h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+typedef struct {
+ int size;
+ HashNode **nodes;
+} HashContext;
+
+static gboolean
+convert_to_hash (gpointer key, gpointer value, gpointer user_data)
+{
+ HashContext *context = user_data;
+ guint hash;
+ HashNode *node;
+
+ hash = icon_name_hash (key) % context->size;
+
+ node = g_new0 (HashNode, 1);
+ node->next = NULL;
+ node->name = key;
+ node->image_list = value;
+
+ if (context->nodes[hash] != NULL)
+ node->next = context->nodes[hash];
+
+ context->nodes[hash] = node;
+
+ return TRUE;
+}
+
+static GHashTable *string_pool = NULL;
+
+static int
+find_string (const char *n)
+{
+ return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
+}
+
+static void
+add_string (const char *n, int offset)
+{
+ g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
+}
+
+static gboolean
+write_string (FILE *cache, const char *n)
+{
+ char *s;
+ int i, l;
+
+ l = ALIGN_VALUE (strlen (n) + 1, 4);
+
+ s = g_malloc0 (l);
+ strcpy (s, n);
+
+ i = fwrite (s, l, 1, cache);
+
+ g_free (s);
+
+ return i == 1;
+
+}
+
+static gboolean
+write_card16 (FILE *cache, guint16 n)
+{
+ int i;
+
+ n = GUINT16_TO_BE (n);
+
+ i = fwrite ((char *)&n, 2, 1, cache);
+
+ return i == 1;
+}
+
+static gboolean
+write_card32 (FILE *cache, guint32 n)
+{
+ int i;
+
+ n = GUINT32_TO_BE (n);
+
+ i = fwrite ((char *)&n, 4, 1, cache);
+
+ return i == 1;
+}
+
+
+static gboolean
+write_image_data (FILE *cache, ImageData *image_data, int offset)
+{
+ guint8 *s;
+ guint len;
+ int i;
+ GdkPixdata *pixdata = &image_data->pixdata;
+
+ /* Type 0 is GdkPixdata */
+ if (!write_card32 (cache, 0))
+ return FALSE;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ s = gdk_pixdata_serialize (pixdata, &len);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+
+ if (!write_card32 (cache, len))
+ {
+ g_free (s);
+ return FALSE;
+ }
+
+ i = fwrite (s, len, 1, cache);
+
+ g_free (s);
+
+ return i == 1;
+}
+
+static gboolean
+write_icon_data (FILE *cache, IconData *icon_data, int offset)
+{
+ int ofs = offset + 12;
+ int j;
+ int tmp, tmp2;
+
+ if (icon_data->has_embedded_rect)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+
+ ofs += 8;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->n_attach_points > 0)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+
+ ofs += 4 + 4 * icon_data->n_attach_points;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->n_display_names > 0)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->has_embedded_rect)
+ {
+ if (!write_card16 (cache, icon_data->x0) ||
+ !write_card16 (cache, icon_data->y0) ||
+ !write_card16 (cache, icon_data->x1) ||
+ !write_card16 (cache, icon_data->y1))
+ return FALSE;
+ }
+
+ if (icon_data->n_attach_points > 0)
+ {
+ if (!write_card32 (cache, icon_data->n_attach_points))
+ return FALSE;
+
+ for (j = 0; j < 2 * icon_data->n_attach_points; j++)
+ {
+ if (!write_card16 (cache, icon_data->attach_points[j]))
+ return FALSE;
+ }
+ }
+
+ if (icon_data->n_display_names > 0)
+ {
+ if (!write_card32 (cache, icon_data->n_display_names))
+ return FALSE;
+
+ ofs += 4 + 8 * icon_data->n_display_names;
+
+ tmp = ofs;
+ for (j = 0; j < 2 * icon_data->n_display_names; j++)
+ {
+ tmp2 = find_string (icon_data->display_names[j]);
+ if (tmp2 == 0 || tmp2 == -1)
+ {
+ tmp2 = tmp;
+ tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
+ /* We're playing a little game with negative
+ * offsets here to handle duplicate strings in
+ * the array.
+ */
+ add_string (icon_data->display_names[j], -tmp2);
+ }
+ else if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ }
+
+ if (!write_card32 (cache, tmp2))
+ return FALSE;
+
+ }
+
+ g_assert (ofs == ftell (cache));
+ for (j = 0; j < 2 * icon_data->n_display_names; j++)
+ {
+ tmp2 = find_string (icon_data->display_names[j]);
+ g_assert (tmp2 != 0 && tmp2 != -1);
+ if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ g_assert (tmp2 == ftell (cache));
+ add_string (icon_data->display_names[j], tmp2);
+ if (!write_string (cache, icon_data->display_names[j]))
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_header (FILE *cache, guint32 dir_list_offset)
+{
+ return (write_card16 (cache, MAJOR_VERSION) &&
+ write_card16 (cache, MINOR_VERSION) &&
+ write_card32 (cache, HASH_OFFSET) &&
+ write_card32 (cache, dir_list_offset));
+}
+
+static int
+get_image_meta_data_size (Image *image)
+{
+ int i;
+
+ /* The complication with storing the size in both
+ * IconData and Image is necessary since we attribute
+ * the size of the IconData only to the first Image
+ * using it (at which time it is written out in the
+ * cache). Later Images just refer to the written out
+ * IconData via the offset.
+ */
+ if (image->icon_data_size == 0)
+ {
+ if (image->icon_data && image->icon_data->size < 0)
+ {
+ IconData *data = image->icon_data;
+
+ data->size = 0;
+
+ if (data->has_embedded_rect ||
+ data->n_attach_points > 0 ||
+ data->n_display_names > 0)
+ data->size += 12;
+
+ if (data->has_embedded_rect)
+ data->size += 8;
+
+ if (data->n_attach_points > 0)
+ data->size += 4 + data->n_attach_points * 4;
+
+ if (data->n_display_names > 0)
+ {
+ data->size += 4 + 8 * data->n_display_names;
+
+ for (i = 0; data->display_names[i]; i++)
+ {
+ int poolv;
+ if ((poolv = find_string (data->display_names[i])) == 0)
+ {
+ data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
+ /* Adding the string to the pool with -1
+ * to indicate that it hasn't been written out
+ * to the cache yet. We still need it in the
+ * pool in case the same string occurs twice
+ * during a get_single_node_size() calculation.
+ */
+ add_string (data->display_names[i], -1);
+ }
+ }
+ }
+
+ image->icon_data_size = data->size;
+ data->size = 0;
+ }
+ }
+
+ g_assert (image->icon_data_size % 4 == 0);
+
+ return image->icon_data_size;
+}
+
+static int
+get_image_pixel_data_size (Image *image)
+{
+ /* The complication with storing the size in both
+ * ImageData and Image is necessary since we attribute
+ * the size of the ImageData only to the first Image
+ * using it (at which time it is written out in the
+ * cache). Later Images just refer to the written out
+ * ImageData via the offset.
+ */
+ if (image->pixel_data_size == 0)
+ {
+ if (image->image_data &&
+ image->image_data->has_pixdata)
+ {
+ image->pixel_data_size = image->image_data->size;
+ image->image_data->size = 0;
+ }
+ }
+
+ g_assert (image->pixel_data_size % 4 == 0);
+
+ return image->pixel_data_size;
+}
+
+static int
+get_image_data_size (Image *image)
+{
+ int len;
+
+ len = 0;
+
+ len += get_image_pixel_data_size (image);
+ len += get_image_meta_data_size (image);
+
+ /* Even if len is zero, we need to reserve space to
+ * write the ImageData, unless this is an .svg without
+ * .icon, in which case both image_data and icon_data
+ * are NULL.
+ */
+ if (len > 0 || image->image_data || image->icon_data)
+ len += 8;
+
+ return len;
+}
+
+static void
+get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
+{
+ GList *list;
+
+ /* Node pointers */
+ *node_size = 12;
+
+ /* Name */
+ if (find_string (node->name) == 0)
+ {
+ *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
+ add_string (node->name, -1);
+ }
+
+ /* Image list */
+ *node_size += 4 + g_list_length (node->image_list) * 8;
+
+ /* Image data */
+ *image_data_size = 0;
+ for (list = node->image_list; list; list = list->next)
+ {
+ Image *image = list->data;
+
+ *image_data_size += get_image_data_size (image);
+ }
+}
+
+static gboolean
+write_bucket (FILE *cache, HashNode *node, int *offset)
+{
+ while (node != NULL)
+ {
+ int node_size, image_data_size;
+ int next_offset, image_data_offset;
+ int data_offset;
+ int name_offset;
+ int name_size;
+ int image_list_offset;
+ int i, len;
+ GList *list;
+
+ g_assert (*offset == ftell (cache));
+
+ node->offset = *offset;
+
+ get_single_node_size (node, &node_size, &image_data_size);
+ g_assert (node_size % 4 == 0);
+ g_assert (image_data_size % 4 == 0);
+ image_data_offset = *offset + node_size;
+ next_offset = *offset + node_size + image_data_size;
+ /* Chain offset */
+ if (node->next != NULL)
+ {
+ if (!write_card32 (cache, next_offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0xffffffff))
+ return FALSE;
+ }
+
+ name_size = 0;
+ name_offset = find_string (node->name);
+ if (name_offset <= 0)
+ {
+ name_offset = *offset + 12;
+ name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
+ add_string (node->name, name_offset);
+ }
+ if (!write_card32 (cache, name_offset))
+ return FALSE;
+
+ image_list_offset = *offset + 12 + name_size;
+ if (!write_card32 (cache, image_list_offset))
+ return FALSE;
+
+ /* Icon name */
+ if (name_size > 0)
+ {
+ if (!write_string (cache, node->name))
+ return FALSE;
+ }
+
+ /* Image list */
+ len = g_list_length (node->image_list);
+ if (!write_card32 (cache, len))
+ return FALSE;
+
+ list = node->image_list;
+ data_offset = image_data_offset;
+ for (i = 0; i < len; i++)
+ {
+ Image *image = list->data;
+ int image_size = get_image_data_size (image);
+
+ /* Directory index */
+ if (!write_card16 (cache, image->dir_index))
+ return FALSE;
+
+ /* Flags */
+ if (!write_card16 (cache, image->flags))
+ return FALSE;
+
+ /* Image data offset */
+ if (image_size > 0)
+ {
+ if (!write_card32 (cache, data_offset))
+ return FALSE;
+ data_offset += image_size;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ list = list->next;
+ }
+
+ /* Now write the image data */
+ list = node->image_list;
+ for (i = 0; i < len; i++, list = list->next)
+ {
+ Image *image = list->data;
+ int pixel_data_size = get_image_pixel_data_size (image);
+ int meta_data_size = get_image_meta_data_size (image);
+
+ if (get_image_data_size (image) == 0)
+ continue;
+
+ /* Pixel data */
+ if (pixel_data_size > 0)
+ {
+ image->image_data->offset = image_data_offset + 8;
+ if (!write_card32 (cache, image->image_data->offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
+ return FALSE;
+ }
+
+ if (meta_data_size > 0)
+ {
+ image->icon_data->offset = image_data_offset + pixel_data_size + 8;
+ if (!write_card32 (cache, image->icon_data->offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
+ return FALSE;
+ }
+
+ if (pixel_data_size > 0)
+ {
+ if (!write_image_data (cache, image->image_data, image->image_data->offset))
+ return FALSE;
+ }
+
+ if (meta_data_size > 0)
+ {
+ if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
+ return FALSE;
+ }
+
+ image_data_offset += pixel_data_size + meta_data_size + 8;
+ }
+
+ *offset = next_offset;
+ node = node->next;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_hash_table (FILE *cache, HashContext *context, int *new_offset)
+{
+ int offset = HASH_OFFSET;
+ int node_offset;
+ int i;
+
+ if (!(write_card32 (cache, context->size)))
+ return FALSE;
+
+ offset += 4;
+ node_offset = offset + context->size * 4;
+ /* Just write zeros here, we will rewrite this later */
+ for (i = 0; i < context->size; i++)
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ /* Now write the buckets */
+ for (i = 0; i < context->size; i++)
+ {
+ if (!context->nodes[i])
+ continue;
+
+ g_assert (node_offset % 4 == 0);
+ if (!write_bucket (cache, context->nodes[i], &node_offset))
+ return FALSE;
+ }
+
+ *new_offset = node_offset;
+
+ /* Now write out the bucket offsets */
+
+ fseek (cache, offset, SEEK_SET);
+
+ for (i = 0; i < context->size; i++)
+ {
+ if (context->nodes[i] != NULL)
+ node_offset = context->nodes[i]->offset;
+ else
+ node_offset = 0xffffffff;
+ if (!write_card32 (cache, node_offset))
+ return FALSE;
+ }
+
+ fseek (cache, 0, SEEK_END);
+
+ return TRUE;
+}
+
+static gboolean
+write_dir_index (FILE *cache, int offset, GList *directories)
+{
+ int n_dirs;
+ GList *d;
+ char *dir;
+ int tmp, tmp2;
+
+ n_dirs = g_list_length (directories);
+
+ if (!write_card32 (cache, n_dirs))
+ return FALSE;
+
+ offset += 4 + n_dirs * 4;
+
+ tmp = offset;
+ for (d = directories; d; d = d->next)
+ {
+ dir = d->data;
+
+ tmp2 = find_string (dir);
+
+ if (tmp2 == 0 || tmp2 == -1)
+ {
+ tmp2 = tmp;
+ tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
+ /* We're playing a little game with negative
+ * offsets here to handle duplicate strings in
+ * the array, even though that should not
+ * really happen for the directory index.
+ */
+ add_string (dir, -tmp2);
+ }
+ else if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ }
+
+ if (!write_card32 (cache, tmp2))
+ return FALSE;
+ }
+
+ g_assert (offset == ftell (cache));
+ for (d = directories; d; d = d->next)
+ {
+ dir = d->data;
+
+ tmp2 = find_string (dir);
+ g_assert (tmp2 != 0 && tmp2 != -1);
+ if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ g_assert (tmp2 == ftell (cache));
+ add_string (dir, tmp2);
+ if (!write_string (cache, dir))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_file (FILE *cache, GHashTable *files, GList *directories)
+{
+ HashContext context;
+ int new_offset;
+
+ /* Convert the hash table into something looking a bit more
+ * like what we want to write to disk.
+ */
+ context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
+ context.nodes = g_new0 (HashNode *, context.size);
+
+ g_hash_table_foreach_remove (files, convert_to_hash, &context);
+
+ /* Now write the file */
+ /* We write 0 as the directory list offset and go
+ * back and change it later */
+ if (!write_header (cache, 0))
+ {
+ g_printerr (_("Failed to write header\n"));
+ return FALSE;
+ }
+
+ if (!write_hash_table (cache, &context, &new_offset))
+ {
+ g_printerr (_("Failed to write hash table\n"));
+ return FALSE;
+ }
+
+ if (!write_dir_index (cache, new_offset, directories))
+ {
+ g_printerr (_("Failed to write folder index\n"));
+ return FALSE;
+ }
+
+ rewind (cache);
+
+ if (!write_header (cache, new_offset))
+ {
+ g_printerr (_("Failed to rewrite header\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+validate_file (const char *file)
+{
+ GMappedFile *map;
+ CacheInfo info;
+
+ map = g_mapped_file_new (file, FALSE, NULL);
+ if (!map)
+ return FALSE;
+
+ info.cache = g_mapped_file_get_contents (map);
+ info.cache_size = g_mapped_file_get_length (map);
+ info.n_directories = 0;
+ info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
+
+ if (!gtk_icon_cache_validate (&info))
+ {
+ g_mapped_file_unref (map);
+ return FALSE;
+ }
+
+ g_mapped_file_unref (map);
+
+ return TRUE;
+}
+
+/**
+ * safe_fclose:
+ * @f: A FILE* stream, must have underlying fd
+ *
+ * Unix defaults for data preservation after system crash
+ * are unspecified, and many systems will eat your data
+ * in this situation unless you explicitly fsync().
+ *
+ * Returns: %TRUE on success, %FALSE on failure, and will set errno()
+ */
+static gboolean
+safe_fclose (FILE *f)
+{
+ int fd = fileno (f);
+ g_assert (fd >= 0);
+ if (fflush (f) == EOF)
+ return FALSE;
+#ifndef G_OS_WIN32
+ if (fsync (fd) < 0)
+ return FALSE;
+#endif
+ if (fclose (f) == EOF)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+build_cache (const char *path)
+{
+ char *cache_path, *tmp_cache_path;
+#ifdef G_OS_WIN32
+ char *bak_cache_path = NULL;
+#endif
+ GHashTable *files;
+ FILE *cache;
+ GStatBuf path_stat, cache_stat;
+ struct utimbuf utime_buf;
+ GList *directories = NULL;
+ int fd;
+ int retry_count = 0;
+#ifndef G_OS_WIN32
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+#else
+ int mode = _S_IWRITE | _S_IREAD;
+#endif
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+ tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+
+opentmp:
+ if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
+ {
+ if (retry_count == 0)
+ {
+ retry_count++;
+ g_remove (tmp_cache_path);
+ goto opentmp;
+ }
+ g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
+ exit (1);
+ }
+
+ cache = fdopen (fd, "wb");
+
+ if (!cache)
+ {
+ g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+ exit (1);
+ }
+
+ files = g_hash_table_new (g_str_hash, g_str_equal);
+ image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ string_pool = g_hash_table_new (g_str_hash, g_str_equal);
+
+ directories = scan_directory (path, NULL, files, NULL, 0);
+
+ if (g_hash_table_size (files) == 0)
+ {
+ /* Empty table, just close and remove the file */
+
+ fclose (cache);
+ g_unlink (tmp_cache_path);
+ g_unlink (cache_path);
+ exit (0);
+ }
+
+ /* FIXME: Handle failure */
+ if (!write_file (cache, files, directories))
+ {
+ g_unlink (tmp_cache_path);
+ exit (1);
+ }
+
+ if (!safe_fclose (cache))
+ {
+ g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+ g_unlink (tmp_cache_path);
+ exit (1);
+ }
+ cache = NULL;
+
+ g_list_free_full (directories, g_free);
+
+ if (!validate_file (tmp_cache_path))
+ {
+ g_printerr (_("The generated cache was invalid.\n"));
+ /*g_unlink (tmp_cache_path);*/
+ exit (1);
+ }
+
+#ifdef G_OS_WIN32
+ if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
+ {
+ bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
+ g_unlink (bak_cache_path);
+ if (g_rename (cache_path, bak_cache_path) == -1)
+ {
+ int errsv = errno;
+
+ g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
+ cache_path, bak_cache_path,
+ g_strerror (errsv),
+ cache_path);
+ g_unlink (cache_path);
+ bak_cache_path = NULL;
+ }
+ }
+#endif
+
+ if (g_rename (tmp_cache_path, cache_path) == -1)
+ {
+ int errsv = errno;
+
+ g_printerr (_("Could not rename %s to %s: %s\n"),
+ tmp_cache_path, cache_path,
+ g_strerror (errsv));
+ g_unlink (tmp_cache_path);
+#ifdef G_OS_WIN32
+ if (bak_cache_path != NULL)
+ if (g_rename (bak_cache_path, cache_path) == -1)
+ {
+ errsv = errno;
+
+ g_printerr (_("Could not rename %s back to %s: %s.\n"),
+ bak_cache_path, cache_path,
+ g_strerror (errsv));
+ }
+#endif
+ exit (1);
+ }
+#ifdef G_OS_WIN32
+ if (bak_cache_path != NULL)
+ g_unlink (bak_cache_path);
+#endif
+
+ /* Update time */
+ /* FIXME: What do do if an error occurs here? */
+ if (g_stat (path, &path_stat) < 0 ||
+ g_stat (cache_path, &cache_stat))
+ exit (1);
+
+ utime_buf.actime = path_stat.st_atime;
+ utime_buf.modtime = cache_stat.st_mtime;
+#if GLIB_CHECK_VERSION (2, 17, 1)
+ g_utime (path, &utime_buf);
+#else
+ utime (path, &utime_buf);
+#endif
+
+ if (!quiet)
+ g_printerr (_("Cache file created successfully.\n"));
+}
+
+static void
+write_csource (const char *path)
+{
+ char *cache_path;
+ char *data;
+ gsize len;
+ int i;
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ if (!g_file_get_contents (cache_path, &data, &len, NULL))
+ exit (1);
+
+ g_printf ("#ifdef __SUNPRO_C\n");
+ g_printf ("#pragma align 4 (%s)\n", var_name);
+ g_printf ("#endif\n");
+
+ g_printf ("#ifdef __GNUC__\n");
+ g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
+ g_printf ("#else\n");
+ g_printf ("static const guint8 %s[] = \n", var_name);
+ g_printf ("#endif\n");
+
+ g_printf ("{\n");
+ for (i = 0; i < len - 1; i++)
+ {
+ if (i %12 == 0)
+ g_printf (" ");
+ g_printf ("0x%02x, ", (guint8)data[i]);
+ if (i % 12 == 11)
+ g_printf ("\n");
+ }
+
+ g_printf ("0x%02x\n};\n", (guint8)data[i]);
+}
+
+static GOptionEntry args[] = {
+ { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
+ { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
+ { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
+ { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
+ { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
+ { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
+ { NULL }
+};
+
+static void
+printerr_handler (const char *string)
+{
+ const char *charset;
+
+ fputs (g_get_prgname (), stderr);
+ fputs (": ", stderr);
+ if (g_get_charset (&charset))
+ fputs (string, stderr); /* charset is UTF-8 already */
+ else
+ {
+ char *result;
+
+ result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
+
+ if (result)
+ {
+ fputs (result, stderr);
+ g_free (result);
+ }
+
+ fflush (stderr);
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ char *path;
+ GOptionContext *context;
+
+ if (argc < 2)
+ return 0;
+
+ g_set_printerr_handler (printerr_handler);
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ context = g_option_context_new ("ICONPATH");
+ g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ path = argv[1];
+#ifdef G_OS_WIN32
+ path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+ if (validate)
+ {
+ char *file = g_build_filename (path, CACHE_NAME, NULL);
+
+ if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
+ {
+ if (!quiet)
+ g_printerr (_("File not found: %s\n"), file);
+ exit (1);
+ }
+ if (!validate_file (file))
+ {
+ if (!quiet)
+ g_printerr (_("Not a valid icon cache: %s\n"), file);
+ exit (1);
+ }
+ else
+ {
+ exit (0);
+ }
+ }
+
+ if (!ignore_theme_index && !has_theme_index (path))
+ {
+ if (path)
+ {
+ g_printerr (_("No theme index file.\n"));
+ }
+ else
+ {
+ g_printerr (_("No theme index file in “%s”.\n"
+ "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
+ }
+
+ return 1;
+ }
+
+ if (!force_update && is_cache_up_to_date (path))
+ return 0;
+
+ replace_backslashes_with_slashes (path);
+ build_cache (path);
+
+ if (strcmp (var_name, "-") != 0)
+ write_csource (path);
+
+ return 0;
+}